home *** CD-ROM | disk | FTP | other *** search
- Subject: v25i007: trn 2.0 - threaded newsreader based on rn 4.4, Part04/13
- Newsgroups: comp.sources.unix
- Approved: vixie@pa.dec.com
-
- Submitted-by: davison@borland.com (Wayne Davison)
- Posting-number: Volume 25, Issue 7
- Archive-name: trn/part04
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 4 (of 13)."
- # Contents: mt-write.c mthreads.8 ngstuff.c nntp/acttimes.c
- # nntp/xthread.patch rthreads.c term.h uudecode.c
- # Wrapped by vixie@cognition.pa.dec.com on Tue Dec 3 16:34:52 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'mt-write.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mt-write.c'\"
- else
- echo shar: Extracting \"'mt-write.c'\" \(9609 characters\)
- sed "s/^X//" >'mt-write.c' <<'END_OF_FILE'
- X/* $Id: mt-write.c,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
- X**
- X** $Log: mt-write.c,v $
- X** Revision 4.4.3.1 1991/11/22 04:12:15 davison
- X** Trn Release 2.0
- X**
- X*/
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#include "threads.h"
- X#include "mthreads.h"
- X
- static FILE *fp_out;
- static int seq;
- static int article_seq;
- X
- static int failure;
- X
- void write_subjects(), write_authors(), write_roots(), write_ids();
- void write_articles(), write_thread(), write_item();
- void enumerate_articles(), enumerate_thread();
- void free_leftovers();
- X
- X/* Write out all the data in a packed format that is easy for our newsreader
- X** to use. We free things as we go, when we don't need them any longer. If
- X** we encounter any write errors, the write_item routine sets a failure flag
- X** to halt our writing of the file, but we keep on plugging away to free
- X** everything up.
- X*/
- int
- write_data(filename)
- char *filename;
- X{
- X if (filename == Nullch) {
- X failure = 2; /* A NULL filename indicates just free the data */
- X } else if ((fp_out = fopen(filename, FOPEN_WB)) == Nullfp) {
- X if (ensure_path(filename)) {
- X if ((fp_out = fopen(filename, FOPEN_WB)) == Nullfp) {
- X log_error("Unable to create file: `%s'.\n", filename);
- X failure = 2;
- X }
- X } else {
- X log_error("Unable to create path: `%s'.\n", filename);
- X failure = 2;
- X }
- X } else {
- X failure = 0;
- X#ifdef SETBUFFER
- X setbuffer(fp_out, rwbuf, RWBUFSIZ);
- X#else
- X# ifdef SETVBUF
- X setvbuf(fp_out, rwbuf, _IOFBF, RWBUFSIZ);
- X# endif
- X#endif
- X }
- X
- X /* If there's no roots, there's no data. Leave the file with no length. */
- X if (!total.root && !failure) {
- X failure = -1;
- X }
- X
- X write_item(&total, sizeof (TOTAL));
- X
- X enumerate_articles();
- X
- X write_authors();
- X write_subjects();
- X write_roots();
- X write_articles();
- X write_ids();
- X free_leftovers();
- X
- X if (failure != 2) {
- X fclose(fp_out);
- X }
- X if (failure == 1) {
- X log_error("Write failed! Removing `%s'.\n", filename);
- X unlink(filename);
- X }
- X return failure <= 0;
- X}
- X
- X/* Recursively descend the article tree, enumerating the articles as we go.
- X** This way we can output the article sequence numbers into the data file.
- X*/
- void
- enumerate_articles()
- X{
- X register ROOT *root;
- X
- X seq = article_seq = 0;
- X
- X for (root = root_root; root; root = root->link) {
- X root->seq = seq++;
- X if (!root->articles) {
- X log_error("** No articles on this root??\n");
- X continue;
- X }
- X enumerate_thread(root->articles);
- X }
- X if (seq != total.root) {
- X log_error("** Wrote %d roots instead of %d **\n", seq, total.root);
- X }
- X if (article_seq != total.article) {
- X log_error("** Wrote %d articles instead of %d **\n", article_seq, total.article);
- X }
- X}
- X
- X/* Recursive routine for above-mentioned enumeration. */
- void
- enumerate_thread(article)
- ARTICLE *article;
- X{
- X while (article) {
- X article->seq = article_seq++;
- X if (article->children) {
- X enumerate_thread(article->children);
- X }
- X article = article->siblings;
- X }
- X}
- X
- X#define write_and_free(str_ptr) /* Comment for makedepend to \
- X ** ignore the backslash above */ \
- X{\
- X register int len = strlen(str_ptr) + 1;\
- X write_item(str_ptr, len);\
- X free(str_ptr);\
- X string_offset += len;\
- X}
- X
- MEM_SIZE string_offset;
- X
- X/* Write out the author information: first the use-counts, then the
- X** name strings all packed together.
- X*/
- void
- write_authors()
- X{
- X register AUTHOR *author;
- X
- X seq = 0;
- X for (author = author_root; author; author = author->link) {
- X write_item(&author->count, sizeof (WORD));
- X author->seq = seq++;
- X }
- X if (seq != total.author) {
- X log_error("** Wrote %d authors instead of %d **\n",
- X seq, total.author);
- X }
- X
- X string_offset = 0;
- X
- X for (author = author_root; author; author = author->link) {
- X write_and_free(author->name);
- X }
- X}
- X
- X/* Write out the subject information: first the packed string data, then
- X** the use-counts. The order is important -- it is the order required
- X** by the roots for their subject structures.
- X*/
- void
- write_subjects()
- X{
- X register ROOT *root;
- X register SUBJECT *subject;
- X
- X for (root = root_root; root; root = root->link) {
- X for (subject = root->subjects; subject; subject = subject->link) {
- X write_and_free(subject->str);
- X }
- X }
- X if (string_offset != total.string1) {
- X log_error("** Author/subject strings were %ld bytes instead of %ld **\n",
- X string_offset, total.string1);
- X }
- X
- X seq = 0;
- X for (root = root_root; root; root = root->link) {
- X for (subject = root->subjects; subject; subject = subject->link) {
- X write_item(&subject->count, sizeof (WORD));
- X subject->seq = seq++;
- X }
- X }
- X if (seq != total.subject) {
- X log_error("** Wrote %d subjects instead of %d **\n",
- X seq, total.subject);
- X }
- X}
- X
- X/* Write the roots in a packed format. Interpret the pointers into
- X** sequence numbers as we go.
- X*/
- void
- write_roots()
- X{
- X register ROOT *root;
- X
- X for (root = root_root; root; root = root->link) {
- X p_root.articles = root->articles->seq;
- X p_root.root_num = root->root_num;
- X p_root.thread_cnt = root->thread_cnt;
- X p_root.subject_cnt = root->subject_cnt;
- X write_item(&p_root, sizeof (PACKED_ROOT));
- X }
- X}
- X
- X#define rel_article(article, rseq) ((article)? (article)->seq - (rseq) : 0)
- X#define valid_seq(ptr) ((ptr)? (ptr)->seq : -1)
- X
- X/* Write all the articles in the same order that we sequenced them. */
- void
- write_articles()
- X{
- X register ROOT *root;
- X
- X for (root = root_root; root; root = root->link) {
- X write_thread(root->articles);
- X }
- X}
- X
- X/* Recursive routine to write the articles in thread order. We depend on
- X** the fact that our first child is the very next article written (if we
- X** have children).
- X*/
- void
- write_thread(article)
- register ARTICLE *article;
- X{
- X while (article) {
- X p_article.num = article->num;
- X p_article.date = article->date;
- X p_article.subject = valid_seq(article->subject);
- X p_article.author = valid_seq(article->author);
- X p_article.flags = article->flags;
- X p_article.child_cnt = article->child_cnt;
- X p_article.parent = rel_article(article->parent, article->seq);
- X p_article.siblings = rel_article(article->siblings, article->seq);
- X p_article.root = article->root->seq;
- X write_item(&p_article, sizeof (PACKED_ARTICLE));
- X if (article->children) {
- X write_thread(article->children);
- X }
- X article = article->siblings;
- X }
- X}
- X
- WORD minus_one = -1;
- X
- X/* Write the message-id strings: each domain name (not including the
- X** ".unknown." domain) followed by all of its associated unique ids.
- X** Then output the article sequence numbers they belong to. This stuff
- X** is last because the newsreader doesn't need to read it.
- X*/
- void
- write_ids()
- X{
- X register DOMAIN *domain;
- X register ARTICLE *id;
- X register DOMAIN *next_domain;
- X register ARTICLE *next_id;
- X
- X string_offset = 0;
- X
- X for (domain = &unk_domain; domain; domain = domain->link) {
- X if (domain != &unk_domain) {
- X write_and_free(domain->name);
- X if (!domain->ids) {
- X log_error("** Empty domain name!! **\n");
- X }
- X }
- X for (id = domain->ids; id; id = id->id_link) {
- X write_and_free(id->id);
- X }
- X }
- X if (string_offset != total.string2) {
- X log_error("** Message-id strings were %ld bytes (%ld) **\n",
- X string_offset, total.string2);
- X }
- X for (domain = &unk_domain; domain; domain = next_domain) {
- X next_domain = domain->link;
- X for (id = domain->ids; id; id = next_id) {
- X next_id = id->id_link;
- X write_item(&id->seq, sizeof (WORD));
- X free(id);
- X }
- X write_item(&minus_one, sizeof (WORD));
- X if (domain != &unk_domain) {
- X free(domain);
- X }
- X }
- X unk_domain.ids = Nullart;
- X unk_domain.link = Null(DOMAIN*);
- X}
- X
- X/* Free everything that's left to free.
- X*/
- void
- free_leftovers()
- X{
- X register ROOT *root, *next_root;
- X register SUBJECT *subj, *next_subj;
- X register AUTHOR *author, *next_author;
- X
- X for (root = root_root; root; root = next_root) {
- X next_root = root->link;
- X for (subj = root->subjects; subj; subj = next_subj) {
- X next_subj = subj->link;
- X free(subj);
- X }
- X free(root);
- X }
- X for (author = author_root; author; author = next_author) {
- X next_author = author->link;
- X free(author);
- X }
- X root_root = Null(ROOT*);
- X author_root = Null(AUTHOR*);
- X}
- X
- X/* This routine will check to be sure that the required path exists for
- X** the data file, and if not it will attempt to create it.
- X*/
- int
- ensure_path(filename)
- register char *filename;
- X{
- X int status, pid, w;
- X char tmpbuf[1024];
- X#ifdef MAKEDIR
- X register char *cp, *last;
- X register char *tbptr = tmpbuf+5;
- X
- X if (!(last = rindex(filename, '/'))) { /* find filename portion */
- X return 1; /* no path, we're fine */
- X }
- X *last = '\0'; /* truncate path at filename */
- X strcpy(tmpbuf, "mkdir");
- X
- X for (cp = last;;) {
- X if (stat(filename, &filestat) >= 0 && (filestat.st_mode & S_IFDIR)) {
- X *cp = '/';
- X break;
- X }
- X if (!(cp = rindex(filename, '/'))) {/* find something that exists */
- X break;
- X }
- X *cp = '\0';
- X }
- X
- X for (cp = filename; cp <= last; cp++) {
- X if (!*cp) {
- X sprintf(tbptr, " %s", filename);
- X tbptr += strlen(tbptr); /* set up for mkdir call */
- X *cp = '/';
- X }
- X }
- X if (tbptr == tmpbuf+5) {
- X return 1;
- X }
- X#else
- X sprintf(tmpbuf,"%s %s %d", file_exp(DIRMAKER), filename, 1);
- X#endif
- X
- X if ((pid = vfork()) == 0) {
- X execl(SH, SH, "-c", tmpbuf, Nullch);
- X _exit(127);
- X }
- X while ((w = wait(&status)) != pid && w != -1) {
- X ;
- X }
- X if (w == -1) {
- X status = -1;
- X }
- X return !status;
- X}
- X
- X/* A simple routine to output some data only if we haven't failed any
- X** previous writes.
- X*/
- void
- write_item(buff, len)
- char *buff;
- int len;
- X{
- X if (!failure) {
- X if (fwrite(buff, 1, len, fp_out) < len) {
- X failure = 1;
- X }
- X }
- X}
- END_OF_FILE
- if test 9609 -ne `wc -c <'mt-write.c'`; then
- echo shar: \"'mt-write.c'\" unpacked with wrong size!
- fi
- # end of 'mt-write.c'
- fi
- if test -f 'mthreads.8' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mthreads.8'\"
- else
- echo shar: Extracting \"'mthreads.8'\" \(10167 characters\)
- sed "s/^X//" >'mthreads.8' <<'END_OF_FILE'
- X.\" $Id: mthreads.8,v 4.4.3.1 1991/11/22 04:13:39 davison Trn $
- X.\"
- X.de Sh
- X.br
- X.ne 5
- X.PP
- X\fB\\$1\fR
- X.PP
- X..
- X.de Sp
- X.if t .sp .5v
- X.if n .sp
- X..
- X.de Ip
- X.br
- X.ie \\n.$>=3 .ne \\$3
- X.el .ne 3
- X.IP "\\$1" \\$2
- X..
- X.\"
- X.\" Set up \*(-- to give an unbreakable dash;
- X.\"
- X.tr \(bs-|\(bv\*(Tr
- X.ie n \{\
- X.ds -- \(bs-
- X.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
- X.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
- X.ds L" ""
- X.ds R" ""
- X.ds L' '
- X.ds R' '
- X'br\}
- X.el\{\
- X.ds -- \(em\|
- X.tr \*(Tr
- X.ds L" ``
- X.ds R" ''
- X.ds L' `
- X.ds R' '
- X'br\}
- X.TH MTHREADS 8 LOCAL
- X.UC 6
- X.SH NAME
- mthreads - threaded database manager for trn
- X.SH SYNOPSIS
- X.B mthreads [-d[MM]] [-e[HHMM]] [-acDfknstvz] [hierarchy_list]
- X.SH DESCRIPTION
- X.I Mthreads
- manages the thread files that are used by the
- X.IR trn (1)
- newsreader.
- X\*(L"Thread files\*(R" are used to store information about the news
- articles and how they are all related to one another.
- X.PP
- X.I Mthreads
- should be run periodically to update the thread database as new news arrives.
- It can be run in single-pass mode (out of cron), in daemon mode, or in a
- combination of the two.
- A site that gets its news feed during the night may just want to run mthreads
- once a day (trn will handle any local postings that show up between passes).
- If more processing is needed, either run mthreads
- more often or run it in daemon mode.
- In daemon mode, a background process is forked off that wakes up every 10
- minutes (by default) to check if the active file has been updated.
- When the mthreads daemon is sleeping between passes, it is possible
- to run an mthreads single pass.
- This is often useful if you wish to run an enhanced expire pass more than
- once a day (see the \-c and \-e options).
- X.SH INSTALLATION
- X.I Mthreads
- in installed in the RNLIB directory chosen during configuration.
- When it is run for the first time, it will automatically create a file called
- X.I active2
- in the MTLIB directory.
- This file is essentially a copy of the active file that keeps the newsgroup
- totals from the last run in one place.
- It is also used to choose which groups are to be processed into thread files.
- All groups start out as \*(L"unthreaded\*(R" unless they are turned on with
- a command like:
- X.IP
- mthreads all
- X.PP
- which would create thread file for all the groups.
- XFor testing purposes it is a good idea to start out small with a command
- like:
- X.IP
- mthreads news
- X.PP
- which would thread only the news hierarchy.
- Thread processing can be turned on or off for individual groups or entire
- hierarchies by specifying the groups in a syntax very similar to that used
- in the sys file.
- XFor example, to turn on all of soc and talk except for talk.politics, and
- to turn off news.lists, use the following command once:
- X.IP
- mthreads soc,talk,!talk.politics,!news.lists
- X.PP
- If mthreads complains that another mthreads process is already running,
- you can use the \-c option to tell it to continue trying to lock instead
- of giving up.
- X.PP
- Once all the desired groups are turned on, the hierarchy list should be
- omitted to allow mthreads to process all enabled groups.
- It can be used, however, in conjunction with the \-a option to customize
- which new groups get turned on as they are created.
- X.SH LOGGING
- As mthreads executes, some status information (including error messages)
- is placed in
- the file mt.log in the MTLIB directory, unless you chose to use SYSLOG.
- This file will grow without bounds, and should be scanned periodically for
- errors, and trimmed in size when it grows too large.
- See the shell script
- X.I mt.check
- for an mt.log maintainer that will send mail if it finds database errors.
- X.SH OPTIONS
- X.TP 5
- X.B \-a
- is used to automatically turn on thread processing for new news groups as
- they are created.
- When this option is specified, the hierarchy list is used to limit
- which new groups get enabled (omitting the hierarchy list is the same
- as specifying \*(L"all\*(R").
- The default without \-a is to leave new groups unthreaded.
- X.TP 5
- X.B \-c
- will continue trying to lock the mthreads database for a single pass
- instead of giving up.
- This is useful for running special commands out of cron while an mthreads
- daemon is active.
- X.TP 5
- X.B \-D
- specifies debug processing.
- Any errors encountered reading a thread file will rename the offending
- file to \*(L"bad.read\*(R".
- Any errors detected while generating a new thread file will rename the
- file to \*(L"bad.write\*(R".
- If more than one 'D' is specified, each group's name is output into
- the log file before it is processed.
- X.TP 5
- X.B \-d
- is used to specify the daemon mode, where
- X.I mthreads
- forks a background task that periodically wakes up and checks for an updated
- active file.
- The number of minutes to wait after the completion of the last pass can
- be specified after the '-d' option (e.g. -d20), otherwise it will default to
- X10 minutes.
- X.TP 5
- X.B \-e
- tells
- X.I mthreads
- to run an enhanced expiration check on the database.
- Without this option, only articles below the minimum field in the active
- file are expired.
- With this option, mthreads will periodically list all the article numbers
- to see which ones actually exist.
- In single-pass mode the
- X.B -e
- option always affects the current pass \*(-- use it
- at lease once a day after expire has run.
- In daemon mode, the
- X.B -e
- option will cause one pass a day to be the enhanced expire pass.
- By default, this is the first time mthreads wakes up after 12:30 am.
- If a different time is desired, it can be specified in the form HHMM
- X(e.g. -e2359).
- X.TP 5
- X.B -f
- is used to force
- X.I mthreads
- to open each and every thread file to see which ones really need to be
- updated, not just the ones that differ in the active/active2 comparison.
- It will also remove any extraneous thread files from unthreaded groups
- X(which should only occur if you manually change the active2 file).
- This option should only be used when manipulating the thread files in
- unorthodox ways.
- X.TP 5
- X.B -k
- can be used to terminate the currently running mthreads daemon, just as if it
- had received a terminate signal.
- When this option is specified, no other activity is performed.
- X.TP 5
- X.B -n
- tells
- X.I mthreads
- that no actual processing of thread files is to be performed.
- This can be used to just adjust which groups are enabled, without
- actually doing any of the processing right away.
- X.TP 5
- X.B -s<usec>
- tells mthreads to sleep for <usec> microseconds before processing each
- article.
- This is useful if your NNTP server cannot handle mthreads running at
- full speed.
- Using
- X.B -s
- by itself will sleep for an entire second to be compatible with older
- versions of mthreads.
- X.TP 5
- X.B -t
- is used to make mthreads update the active.times file (as specified
- during configuration) with new directory names as they are encountered.
- Don't use this option if your news software maintains this file for
- you (as C news does).
- X.TP 5
- X.B -v
- selects additional levels of verbosity in the log file.
- The default (without -v) is to log mthread's startup, the totals for each
- pass, and major database errors.
- Add one
- X.B -v
- to get extra reference line problems logged into the file.
- Add a second and a third for even more (useless?) information.
- A fourth will cause mthreads to output each group's name into the log file
- before it is processed.
- X.TP 5
- X.B -z
- tells mthreads to 'zap' any thread file it believes to be corrupt.
- This will allow the file to be regenerated from scratch on the next pass.
- X.TP 5
- X.B hierarchy_list
- The hierarchy list is used to turn thread processing on or off for the listed
- groups while limiting itself to updating only the listed groups.
- If specified with the \-a option, however, it only limits which new groups
- get enabled.
- The groups are specified in a manner very similar to the news software's
- sys file: \*(L"news\*(R" matches all groups in news; \*(L"!news\*(R" excludes
- all groups in news; \*(L"comp.all.ibm.pc,!comp.all.ibm.pc.all\*(L" matches both
- comp.sys.ibm.pc and comp.binaries.ibm.pc, but not comp.binaries.ibm.pc.d.
- X.SH OUTPUT
- When
- X.I mthreads
- is run in single-pass mode it generates a stream a status characters on
- stdout that present a visual display of what is happening. If
- single-pass mode is used for regular processing, this output can be
- redirected to /dev/null.
- X.Sp
- The output definitions:
- X.br
- X \&'.' = group's entry is up to date
- X.br
- X \&':' = group processed \*(-- no change
- X.br
- X \&'#' = group processed
- X.br
- X \&'-' = group processed \*(-- is now empty
- X.br
- X \&'x' = group excluded in active
- X.br
- X \&'X' = group excluded in active2
- X.br
- X \&'*' = chdir failed (might be ok)
- X.br
- X \&'!' = write failed (is NOT ok)
- X.br
- X \&'e' = informational error
- X.br
- X \&'E' = database-affecting error
- X.SH CONFIGURATION
- During the configuration of
- X.IR trn ,
- a choice was made about where to place the thread data files.
- They either exist as a .thread file in each group's spool directory, or they
- are each a group.th file in a one-off directory structure on another drive.
- See the THREAD_DIR definition in config.h to review or change this definition.
- X.SH REBUILDING
- If the thread files are ever removed, also remove the file db.init in
- the THREAD_DIR.
- This file contains the byte-order of the machine that generated the database,
- and needs to be removed to truly start from scratch.
- An easy way to get
- X.I mthreads
- to remove all the files except for db.init is to specify the command:
- X.IP
- mthreads !all
- X.PP
- This also turns off thread processing for all groups.
- X.SH "ERROR HANDLING"
- If the active2 file is removed or corrupted, it will
- be automatically rebuilt in the normal course of operation.
- The record of which groups should be threaded will be lost, however.
- Missing/corrupted thread files are automatically re-built.
- X.SH EXAMPLES
- Recommended commands to run on a regular basis are:
- X.IP
- mthreads -dave0630
- X.PP
- to start up an mthreads daemon in verbose logging mode that automatically
- threads new groups and performs an extended expire at 6:30 am, or:
- X.IP
- mthreads -e >/dev/null
- X.PP
- to run an mthreads single-pass with extended expire that leaves new groups
- unthreaded.
- X.SH FILES
- X/usr/lib/news/active
- X.br
- X$MTLIB/active2
- X.br
- X$MTLIB/mt.log
- X.br
- X$THREAD_DIR/db.init
- X.br
- X$MTLIB/LOCKmthreads
- X.br
- Lots of thread data files.
- X.SH AUTHOR
- Wayne Davison <davison@borland.com>
- END_OF_FILE
- if test 10167 -ne `wc -c <'mthreads.8'`; then
- echo shar: \"'mthreads.8'\" unpacked with wrong size!
- fi
- # end of 'mthreads.8'
- fi
- if test -f 'ngstuff.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ngstuff.c'\"
- else
- echo shar: Extracting \"'ngstuff.c'\" \(11114 characters\)
- sed "s/^X//" >'ngstuff.c' <<'END_OF_FILE'
- X/* $Id: ngstuff.c,v 4.4 1991/09/09 20:23:31 sob Exp sob $
- X *
- X * $Log: ngstuff.c,v $
- X * Revision 4.4 1991/09/09 20:23:31 sob
- X * release 4.4
- X *
- X *
- X *
- X */
- X/* This software is Copyright 1991 by Stan Barber.
- X *
- X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
- X * use this software as long as: there is no monetary profit gained
- X * specifically from the use or reproduction of this software, it is not
- X * sold, rented, traded or otherwise marketed, and this copyright notice is
- X * included prominently in any copy made.
- X *
- X * The author make no claims as to the fitness or correctness of this software
- X * for any use whatsoever, and it is provided as is. Any use of this software
- X * is at the user's own risk.
- X */
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#include "term.h"
- X#include "util.h"
- X#include "ng.h"
- X#include "bits.h"
- X#include "intrp.h"
- X#include "cheat.h"
- X#include "head.h"
- X#include "final.h"
- X#include "sw.h"
- X#ifdef USETHREADS
- X#include "threads.h"
- X#include "rthreads.h"
- X#include "rn.h"
- X#include "rcstuff.h"
- X#endif
- X#include "decode.h"
- X#include "INTERN.h"
- X#include "ngstuff.h"
- X
- void
- ngstuff_init()
- X{
- X ;
- X}
- X
- X/* do a shell escape */
- X
- int
- escapade()
- X{
- X register char *s;
- X bool interactive = (buf[1] == FINISHCMD);
- X bool docd;
- X char whereiam[512];
- X
- X if (!finish_command(interactive)) /* get remainder of command */
- X return -1;
- X s = buf+1;
- X docd = *s != '!';
- X if (!docd) {
- X s++;
- X }
- X else {
- X getwd(whereiam);
- X if (chdir(cwd)) {
- X printf(nocd,cwd) FLUSH;
- X sig_catcher(0);
- X }
- X }
- X while (*s == ' ') s++;
- X /* skip leading spaces */
- X interp(cmd_buf, (sizeof cmd_buf), s);/* interpret any % escapes */
- X resetty(); /* make sure tty is friendly */
- X doshell(Nullch,cmd_buf); /* invoke the shell */
- X noecho(); /* and make terminal */
- X crmode(); /* unfriendly again */
- X if (docd) {
- X if (chdir(whereiam)) {
- X printf(nocd,whereiam) FLUSH;
- X sig_catcher(0);
- X }
- X }
- X#ifdef MAILCALL
- X mailcount = 0; /* force recheck */
- X#endif
- X return 0;
- X}
- X
- X/* process & command */
- X
- int
- switcheroo()
- X{
- X if (!finish_command(TRUE)) /* get rest of command */
- X return -1; /* if rubbed out, try something else */
- X if (!buf[1])
- X pr_switches();
- X#ifdef PUSHBACK
- X else if (buf[1] == '&') {
- X if (!buf[2]) {
- X page_init();
- X show_macros();
- X }
- X else {
- X char tmpbuf[LBUFLEN];
- X register char *s;
- X
- X for (s=buf+2; isspace(*s); s++);
- X mac_line(s,tmpbuf,(sizeof tmpbuf));
- X }
- X }
- X#endif
- X else {
- X bool docd = (instr(buf,"-d", TRUE) != Nullch);
- X char whereami[512];
- X
- X if (docd)
- X getwd(whereami);
- X sw_list(buf+1);
- X if (docd) {
- X cwd_check();
- X if (chdir(whereami)) { /* -d does chdirs */
- X printf(nocd,whereami) FLUSH;
- X sig_catcher(0);
- X }
- X }
- X }
- X return 0;
- X}
- X
- X/* process range commands */
- X
- int
- numnum()
- X{
- X ART_NUM min, max;
- X char *cmdlst = Nullch;
- X register char *s, *c;
- X ART_NUM oldart = art;
- X char tmpbuf[LBUFLEN];
- X bool justone = TRUE; /* assume only one article */
- X
- X perform_cnt = 0;
- X if (!finish_command(TRUE)) /* get rest of command */
- X return NN_INP;
- X if (lastart < 1) {
- X fputs("\nNo articles\n",stdout) FLUSH;
- X return NN_ASK;
- X }
- X#ifdef ARTSRCH
- X if (srchahead)
- X srchahead = -1;
- X#endif
- X for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++)
- X if (!isdigit(*s))
- X justone = FALSE;
- X if (*s) {
- X cmdlst = savestr(s);
- X justone = FALSE;
- X }
- X else if (!justone)
- X cmdlst = savestr("m");
- X *s++ = ',';
- X *s = '\0';
- X safecpy(tmpbuf,buf,LBUFLEN);
- X for (s = tmpbuf; c = index(s,','); s = ++c) {
- X *c = '\0';
- X if (*s == '.')
- X min = oldart;
- X else
- X min = atol(s);
- X#ifdef USETHREADS
- X if (min<absfirst && justone) {
- X int r;
- X
- X /* Check if this is a root number */
- X for (r = 0; r < total.root; r++) {
- X if (p_roots[r].root_num == min) {
- X p_art = p_articles + p_roots[r].articles;
- X art = p_art->num;
- X if (p_art->subject == -1) {
- X follow_thread('N');
- X }
- X return NN_REREAD;
- X }
- X }
- X }
- X#endif
- X if (min<absfirst) { /* make sure it is reasonable */
- X min = absfirst;
- X printf("(First article is %ld)\n",(long)absfirst) FLUSH;
- X pad(just_a_sec/3);
- X }
- X if ((s=index(s,'-')) != Nullch) {
- X s++;
- X if (*s == '$')
- X max = lastart;
- X else if (*s == '.')
- X max = oldart;
- X else
- X max = atol(s);
- X }
- X else
- X max = min;
- X if (max>lastart) {
- X max = lastart;
- X if (min > max)
- X min = max;
- X printf("(Last article is %ld)\n",(long)lastart) FLUSH;
- X pad(just_a_sec/3);
- X }
- X if (max < min) {
- X fputs("\nBad range\n",stdout) FLUSH;
- X if (cmdlst)
- X free(cmdlst);
- X return NN_ASK;
- X }
- X if (justone) {
- X art = min;
- X return NN_REREAD;
- X }
- X check_first(min);
- X for (art=min; art<=max; art++) {
- X if (perform(cmdlst,TRUE)) {
- X#ifdef VERBOSE
- X IF(verbose)
- X printf("\n(Interrupted at article %ld)\n",(long)art)
- X FLUSH;
- X ELSE
- X#endif
- X#ifdef TERSE
- X printf("\n(Intr at %ld)\n",(long)art) FLUSH;
- X#endif
- X if (cmdlst)
- X free(cmdlst);
- X return NN_ASK;
- X }
- X }
- X }
- X art = oldart;
- X if (cmdlst)
- X free(cmdlst);
- X return NN_NORM;
- X}
- X
- X#ifdef USETHREADS
- int
- use_selected()
- X{
- X PACKED_ARTICLE *root_limit;
- X register char ch;
- X register int r;
- X char *cmdstr;
- X int ret = 1, orig_root_cnt = selected_root_cnt;
- X
- X if (!finish_command(TRUE)) /* get rest of command */
- X return 0;
- X if (!(ch = buf[1]))
- X return -1;
- X cmdstr = savestr(buf+1);
- X
- X perform_cnt = 0;
- X page_line = 1;
- X
- X /* Multiple commands and commands that operate on individual articles
- X ** use the article loop.
- X */
- X if (strlen(cmdstr) > 1 || index("ejmMsSwW|=", ch)) {
- X bool want_unread = (unread_selector || ch == 'm');
- X
- X for (r = 0; r < total.root; r++) {
- X if (scan_all_roots
- X || (!orig_root_cnt&&root_article_cnts[r]&&!(selected_roots[r]&4))
- X || (selected_roots[r] & (unread_selector+1))) {
- X p_art = p_articles + p_roots[r].articles;
- X root_limit = upper_limit( p_art, 0 );
- X for (; p_art < root_limit; p_art++) {
- X art = p_art->num;
- X if (p_art->subject != -1
- X && (!was_read(art) ^ want_unread)) {
- X if (perform(cmdstr, TRUE)) {
- X fputs("\nInterrupted\n", stdout) FLUSH;
- X goto break_out;
- X }
- X }
- X if (p_art == Nullart)
- X break;
- X }/* for all articles */
- X }/* if selected */
- X }/* for all threads */
- X } /* other commands get the root loop */
- X else if (ch == '+' || ch == '-' || ch == 'J' || ch == 'T' || ch == 't') {
- X for (r = 0; r < total.root; r++) {
- X if (scan_all_roots
- X || (!orig_root_cnt&&root_article_cnts[r]&&!(selected_roots[r]&4))
- X || (selected_roots[r] & (unread_selector+1))) {
- X if (mode != 't' && ch != 't') {
- X printf("T%-5ld ", (long)p_roots[r].root_num);
- X }
- X p_art = p_articles + p_roots[r].articles;
- X art = p_art->num;
- X if (perform(cmdstr, FALSE)) {
- X fputs("\nInterrupted\n", stdout) FLUSH;
- X goto break_out;
- X }
- X#ifdef VERBOSE
- X IF(verbose)
- X if (mode != 't' && ch != 't' && ch != 'T')
- X putchar('\n') FLUSH;
- X#endif
- X }
- X }
- X }
- X else if (ch == 'E') { /* one command needs no looping at all */
- X if (decode_fp) {
- X decode_end();
- X } else {
- X ret = 2;
- X }
- X }
- X else {
- X printf("???%s\n",cmdstr);
- X ret = -1;
- X }
- X break_out:
- X free(cmdstr);
- X return ret;
- X}
- X#endif
- X
- int
- perform(cmdlst,toplevel)
- register char *cmdlst;
- int toplevel;
- X{
- X register int ch;
- X
- X if (toplevel) {
- X printf("%-6ld ",art);
- X fflush(stdout);
- X }
- X perform_cnt++;
- X for (; ch = *cmdlst; cmdlst++) {
- X if (isspace(ch) || ch == ':')
- X continue;
- X if (ch == 'j') {
- X if (!was_read(art)) {
- X mark_as_read();
- X#ifdef VERBOSE
- X IF(verbose)
- X fputs("\tJunked",stdout);
- X#endif
- X }
- X#ifdef USETHREADS
- X else if (unread_selector)
- X goto unselect_it;
- X#endif
- X }
- X#ifdef USETHREADS
- X else if (ch == '+') {
- X register char mask = unread_selector+1;
- X find_article(art);
- X if (p_art && !(selected_roots[p_art->root] & mask)) {
- X register int r = p_art->root;
- X selected_roots[r] |= mask;
- X selected_root_cnt++;
- X if (mode == 't') {
- X selected_count += root_article_cnts[r];
- X } else {
- X selected_count += count_one_root(r);
- X#ifdef VERBOSE
- X IF(verbose)
- X fputs("\tSelected",stdout);
- X#endif
- X }
- X }
- X }
- X else if (ch == '-') {
- X register char mask;
- X unselect_it:
- X mask = unread_selector+1;
- X find_article(art);
- X if (p_art && selected_root_cnt
- X && (selected_roots[p_art->root] & mask)) {
- X register int r = p_art->root;
- X selected_roots[r] &= ~mask;
- X if (unread_selector)
- X selected_roots[r] |= 4;
- X selected_root_cnt--;
- X if (mode == 't') {
- X selected_count -= root_article_cnts[r];
- X } else {
- X selected_count -= count_one_root(r);
- X#ifdef VERBOSE
- X IF(verbose)
- X fputs("\tDeselected",stdout);
- X#endif
- X }
- X }
- X }
- X else if (ch == 't') {
- X find_article(art);
- X entire_tree();
- X }
- X else if (ch == 'J' || ch == 'T' || ch == ',') {
- X char tmpbuf[128];
- X ART_NUM oldart = art;
- X
- X find_article(art);
- X if (ch == ',')
- X mark_as_read();
- X if (p_art) {
- X if (ch == 'T') {
- X sprintf(tmpbuf,"T%ld\t# %s",
- X (long)p_roots[p_art->root].root_num,
- X subject_ptrs[p_art->subject]);
- X fputs(tmpbuf,stdout);
- X kf_append(tmpbuf);
- X }
- X follow_thread(ch == ',' ? 'K' : 'J');
- X art = oldart;
- X }
- X }
- X#endif
- X else if (ch == 'm') {
- X if (was_read(art)) {
- X unmark_as_read();
- X#ifdef VERBOSE
- X IF(verbose)
- X fputs("\tMarked unread",stdout);
- X#endif
- X }
- X }
- X else if (ch == 'M') {
- X#ifdef DELAYMARK
- X delay_unmark(art);
- X#ifdef VERBOSE
- X IF(verbose)
- X fputs("\tWill return",stdout);
- X#endif
- X#else
- X notincl("M");
- X return -1;
- X#endif
- X }
- X else if (ch == '=') {
- X printf("\t%s",fetchsubj(art,FALSE,FALSE));
- X#ifdef VERBOSE
- X IF(verbose)
- X ;
- X ELSE
- X#endif
- X putchar('\n') FLUSH; /* ghad! */
- X }
- X else if (ch == 'C') {
- X#ifdef ASYNC_PARSE
- X printf("\t%sancelled",(cancel_article() ? "Not c" : "C"));
- X#else
- X notincl("C");
- X return -1;
- X#endif
- X }
- X else if (ch == '%') {
- X#ifdef ASYNC_PARSE
- X char tmpbuf[512];
- X
- X if (one_command)
- X interp(tmpbuf, (sizeof tmpbuf), cmdlst);
- X else
- X cmdlst = dointerp(tmpbuf, (sizeof tmpbuf), cmdlst, ":") - 1;
- X perform_cnt--;
- X if (perform(tmpbuf,FALSE))
- X return -1;
- X#else
- X notincl("%");
- X return -1;
- X#endif
- X }
- X else if (index("!&sSwWe|",ch)) {
- X if (one_command)
- X strcpy(buf,cmdlst);
- X else
- X cmdlst = cpytill(buf,cmdlst,':') - 1;
- X /* we now have the command in buf */
- X if (ch == '!') {
- X escapade();
- X#ifdef VERBOSE
- X IF(verbose)
- X fputs("\tShell escaped",stdout);
- X#endif
- X }
- X else if (ch == '&') {
- X switcheroo();
- X#ifdef VERBOSE
- X IF(verbose)
- X if (buf[1] && buf[1] != '&')
- X fputs("\tSwitched",stdout);
- X#endif
- X }
- X else {
- X putchar('\t');
- X save_article();
- X#ifdef VERBOSE
- X IF(verbose)
- X ;
- X ELSE
- X#endif
- X putchar('\n') FLUSH;
- X }
- X }
- X else {
- X printf("\t???%s\n",cmdlst);
- X return -1;
- X }
- X#ifdef VERBOSE
- X fflush(stdout);
- X#endif
- X if (one_command)
- X break;
- X }
- X if (toplevel) {
- X#ifdef VERBOSE
- X IF(verbose)
- X putchar('\n') FLUSH;
- X#endif
- X }
- X if( int_count ) {
- X int_count = 0;
- X return -1;
- X }
- X return 0;
- X}
- END_OF_FILE
- if test 11114 -ne `wc -c <'ngstuff.c'`; then
- echo shar: \"'ngstuff.c'\" unpacked with wrong size!
- fi
- # end of 'ngstuff.c'
- fi
- if test -f 'nntp/acttimes.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'nntp/acttimes.c'\"
- else
- echo shar: Extracting \"'nntp/acttimes.c'\" \(10481 characters\)
- sed "s/^X//" >'nntp/acttimes.c' <<'END_OF_FILE'
- X/* $Header: acttimes.c,v 1.1 91/11/02 18:38:29 davison Exp $
- X**
- X** $Log: acttimes.c,v $
- X*/
- X
- X/* This program will maintain the file active.times if your news software
- X** doesn't already do this for you. The file contains a list of newsgroup
- X** names followed by the time of creation (in seconds since 1970) and the
- X** address of the creator. Since we can't tell who actually created the
- X** group, they will all indicate acttimes@DOMAIN.
- X**
- X** Place this file in your NNTP support directory and change your
- X** common/conf.h to undef NGDATE_FILE and STAT_FILE, and define
- X** ACTIVE_TIMES_FILE. To make, you can replace every mention of
- X** "mkgrdates" in the support/Makefile with "acttimes" and then
- X** type "make".
- X**
- X** To use this without having NNTP around, undefine the NNTP_SUPPORT
- X** define, edit the other defines that follow to indicate your setup,
- X** and compile it with something like "cc -O -o acttimes acttimes.c".
- X**
- X** Use either "acttimes -d" to start a daemon process that wakes up every
- X** 10 minutes (by default) to check if the active file is a different
- X** size, or put "acttimes" into your cron file to be run periodically.
- X*/
- X
- X#define NNTP_SUPPORT /* comment out if not using NNTP */
- X
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <signal.h>
- X#include <errno.h>
- X#ifdef NNTP_SUPPORT
- X#include "../common/conf.h"
- X#endif
- X#ifdef USG
- X#include <time.h>
- X#else
- X#include <sys/time.h>
- X#endif
- X
- X/* ---------- Start of configuration defines ---------- */
- X
- X#ifndef USG
- X#define TERMIO /* Is this is termio system? */
- X#endif
- X
- X/* NNTP sites have the following already defined in ../common/conf.h */
- X
- X#ifndef DOMAIN /* our domain name */
- X#define DOMAIN "local"
- X#endif
- X
- X#ifndef ACTIVE_FILE /* the active file for your news system */
- X#define ACTIVE_FILE "/usr/lib/news/active"
- X#endif
- X
- X#ifndef ACTIVE_TIMES_FILE /* the name of the file to update */
- X#define ACTIVE_TIMES_FILE "/usr/lib/news/active.times"
- X#endif
- X
- X#ifndef SIGRET /* set this to "int" if you have problems */
- X#define SIGRET void
- X#endif
- X
- X#ifndef MAXPATHLEN /* you'll probably want to leave this alone */
- X#define MAXPATHLEN 1024
- X#endif
- X
- X/*#define index strchr /* uncomment these if you need them */
- X/*#define rindex strrchr /* (i.e. if index is undefined) */
- X
- X/* ---------- End of configuration defines ---------- */
- X
- X#ifdef TERMIO
- X#include <termio.h>
- X#else
- X#include <sgtty.h>
- X#endif
- X
- X#define TIMER_FIRST 1
- X#define TIMER_DEFAULT (10 * 60)
- X
- X#define strnEQ(x,y,n) (!strncmp((x),(y),(n)))
- X
- extern errno;
- X
- char *index(), *rindex(), *malloc();
- SIGRET alarm_handler(), quit_handler();
- void active_times(), free_lines(), wrap_it_up();
- X
- typedef struct _active_line {
- X struct _active_line *link;
- X char *name;
- X char type;
- X} ACTIVE_LINE;
- X
- ACTIVE_LINE *line_root = NULL, *last_line = NULL, *pline = NULL;
- long last_actsize;
- int daemon_delay = 0, kill_daemon = 0, old_groups = 0;
- X
- XFILE *fp_lock;
- X
- struct stat filestat;
- X
- char buf[MAXPATHLEN];
- char lockfile[MAXPATHLEN];
- X
- main(argc, argv)
- int argc;
- char *argv[];
- X{
- X int fd;
- X long pid;
- X char *cp;
- X
- X while (--argc) {
- X if (**++argv == '-') {
- X while (*++*argv) {
- X switch (**argv) {
- X case 'd': /* run in daemon mode */
- X if (*++*argv <= '9' && **argv >= '0') {
- X daemon_delay = atoi(*argv) * 60;
- X while (*++*argv <= '9' && **argv >= '0') {
- X ;
- X }
- X } else {
- X daemon_delay = TIMER_DEFAULT;
- X }
- X --*argv;
- X break;
- X case 'k': /* kill running acttimes */
- X kill_daemon++;
- X break;
- X default:
- X fprintf(stderr, "Unknown option: '%c'\n", **argv);
- X exit(1);
- X }
- X }
- X } else {
- X fprintf(stderr,
- X "Usage: acttimes [-d<mins>]\nOr: acttimes -k\n");
- X exit(1);
- X }
- X }
- X
- X /* Set up a nice friendly umask. */
- X umask(002);
- X
- X /* Make sure we're not already running by creating a lock file. */
- X strcpy(lockfile, ACTIVE_TIMES_FILE);
- X if ((cp = rindex(lockfile, '/')) != 0) {
- X cp++;
- X } else {
- X cp = lockfile;
- X }
- X *cp = '\0';
- X sprintf(buf, "%sLOCK.%ld", lockfile, (long)getpid());
- X if ((fp_lock = fopen(buf, "w")) == 0) {
- X fprintf(stderr, "Unable to create lock temporary `%s'.\n", buf);
- X exit(1);
- X }
- X fprintf(fp_lock, "%ld\n", (long)getpid());
- X fclose(fp_lock);
- X
- X /* Try to link to lock file. */
- X strcat(lockfile, "LOCKacttimes");
- X dolink:
- X if (link(buf, lockfile) < 0) {
- X long otherpid;
- X /* Try to avoid possible race with daemon starting up. */
- X sleep (5);
- X if ((fp_lock = fopen(lockfile, "r")) == 0) {
- X fprintf(stderr, "unable to open %s\n", lockfile);
- X unlink(buf);
- X exit(1);
- X }
- X if (fscanf(fp_lock, "%ld", &otherpid) != 1) {
- X fprintf(stderr, "unable to read pid from %s\n", lockfile);
- X unlink(buf);
- X fclose(fp_lock);
- X exit(1);
- X }
- X fclose(fp_lock);
- X if (kill(otherpid, kill_daemon ? SIGTERM : 0) == -1
- X && errno == ESRCH) {
- X if (unlink(lockfile) == -1) {
- X fprintf(stderr, "unable to unlink lockfile %s\n", lockfile);
- X unlink(buf);
- X exit(1);
- X }
- X if (!kill_daemon) {
- X goto dolink;
- X }
- X }
- X unlink(buf);
- X if (kill_daemon) {
- X fprintf(stderr, "killing currently running acttimes.\n");
- X exit(0);
- X } else {
- X fprintf(stderr, "acttimes is already running.\n");
- X exit(1);
- X }
- X }
- X
- X unlink(buf); /* remove temporary LOCK.<pid> file */
- X
- X if (kill_daemon) {
- X fprintf(stderr, "acttimes is not running.\n");
- X wrap_it_up(1);
- X }
- X
- X#ifdef SIGHUP
- X if( signal( SIGHUP, SIG_IGN ) != SIG_IGN ) {
- X signal( SIGHUP, quit_handler );
- X }
- X#endif
- X if( signal( SIGINT, SIG_IGN ) != SIG_IGN ) {
- X signal( SIGINT, quit_handler );
- X }
- X#ifdef SIGQUIT
- X if( signal( SIGQUIT, SIG_IGN ) != SIG_IGN ) {
- X signal( SIGQUIT, quit_handler );
- X }
- X#endif
- X signal( SIGTERM, quit_handler );
- X#ifdef SIGTTIN
- X signal( SIGTTIN, SIG_IGN );
- X signal( SIGTTOU, SIG_IGN );
- X#endif
- X signal( SIGALRM, SIG_IGN );
- X
- X /* If we're not in daemon mode, run through once and quit. */
- X if (!daemon_delay) {
- X active_times();
- X } else {
- X /* For daemon mode, we cut ourself off from anything tty-related and
- X ** run in the background (involves forks, but no knives).
- X */
- X close(0);
- X if (open("/dev/null", 2) != 0) {
- X fprintf(stderr, "unable to open /dev/null!\n");
- X wrap_it_up(1);
- X }
- X close(1);
- X close(2);
- X dup(0);
- X dup(0);
- X while ((pid = fork()) < 0) {
- X sleep(2);
- X }
- X if (pid) {
- X exit(0);
- X }
- X#ifdef TIOCNOTTY
- X if ((fd = open("/dev/tty", 1)) >= 0) {
- X ioctl(fd, TIOCNOTTY, (int*)0);
- X close(fd);
- X }
- X#else
- X (void) setpgrp();
- X while ((pid = fork()) < 0) {
- X sleep(2);
- X }
- X if (pid) {
- X exit(0);
- X }
- X#endif
- X /* Put our pid in the lock file for death detection */
- X if( (fp_lock = fopen(lockfile, "w")) != 0) {
- X fprintf(fp_lock, "%ld\n", (long)getpid());
- X fclose(fp_lock);
- X }
- X
- X signal(SIGALRM, alarm_handler);
- X
- X /* Start timer -- first interval is shorter than all others */
- X alarm(TIMER_FIRST);
- X for (;;) {
- X pause(); /* let alarm go off */
- X alarm(0);
- X
- X if (stat(ACTIVE_FILE, &filestat) < 0) {
- X fprintf(stderr, "Unable to stat active file -- quitting.\n");
- X wrap_it_up(1);
- X }
- X if (filestat.st_size != last_actsize) {
- X last_actsize = filestat.st_size;
- X active_times();
- X }
- X alarm(daemon_delay);
- X } /* for */
- X }/* if */
- X
- X wrap_it_up(0);
- X}
- X
- SIGRET
- alarm_handler()
- X{
- X signal(SIGALRM, alarm_handler);
- X}
- X
- SIGRET
- quit_handler()
- X{
- X wrap_it_up(0);
- X}
- X
- void
- wrap_it_up(ret)
- X{
- X unlink(lockfile);
- X exit(ret);
- X}
- X
- void
- active_times()
- X{
- X FILE *fp_active, *fp_date_r, *fp_date_w;
- X register char *cp;
- X
- X if ((fp_active = fopen(ACTIVE_FILE, "r")) == NULL) {
- X if (!daemon_delay) {
- X fprintf(stderr, "Unable to open active file.\n");
- X }
- X return;
- X }
- X if ((fp_date_r = fopen(ACTIVE_TIMES_FILE, "r")) == NULL) {
- X if (!daemon_delay) {
- X fprintf(stderr, "Creating active.times file.\n");
- X }
- X old_groups = 1;
- X } else {
- X old_groups = 0;
- X }
- X sprintf(buf, "%s.n", ACTIVE_TIMES_FILE);
- X if ((fp_date_w = fopen(buf, "w")) == NULL) {
- X if (!daemon_delay) {
- X fprintf(stderr, "Unable to create active.times file.\n");
- X }
- X return;
- X }
- X
- X /* Loop through entire active file and remember each line. */
- X while (fgets(buf, sizeof buf, fp_active)) {
- X if (!(cp = index(buf, ' ')) || cp[1] == '\0') {
- X continue;
- X }
- X cp[1] = '\0'; /* include trailing space */
- X if (!(cp = rindex(cp + 2, ' '))) {
- X continue;
- X }
- X pline = (ACTIVE_LINE*)malloc(sizeof (ACTIVE_LINE));
- X if (!pline) {
- X if (line_root) {
- X last_line->link = NULL;
- X free_lines();
- X }
- X bug_out:
- X fclose(fp_active);
- X fclose(fp_date_r);
- X fclose(fp_date_w);
- X return;
- X }
- X pline->name = malloc(strlen(buf) + 1);
- X if (!pline->name) {
- X if (line_root) {
- X last_line->link = NULL;
- X pline->name = NULL;
- X free_lines();
- X } else {
- X free(pline);
- X }
- X goto bug_out;
- X }
- X strcpy(pline->name, buf);
- X pline->type = cp[1];
- X if (!last_line) {
- X line_root = pline;
- X } else {
- X last_line->link = pline;
- X }
- X last_line = pline;
- X }
- X last_line->link = NULL;
- X fclose(fp_active);
- X
- X if (fp_date_r) {
- X /* Loop through date file, copying existing groups to new file. */
- X while (fgets(buf, sizeof buf, fp_date_r)) {
- X last_line = NULL;
- X for (pline = line_root; pline; pline = pline->link) {
- X if (strnEQ(buf, pline->name, strlen(pline->name))) {
- X fputs(buf, fp_date_w);
- X free(pline->name);
- X if (last_line) {
- X last_line->link = pline->link;
- X } else {
- X line_root = pline->link;
- X }
- X free(pline);
- X break;
- X }
- X last_line = pline;
- X }/* for */
- X }
- X }
- X /* Remaining entries from active file are new groups. */
- X for (pline = line_root; pline; pline = last_line) {
- X if (pline->type != 'x' && pline->type != '=') {
- X /* If it's not 'x'ed out, write it out with the current time. */
- X fprintf(fp_date_w, "%s%ld acttimes@%s\n", pline->name,
- X old_groups ? 30010440L : time(NULL), DOMAIN);
- X }
- X free(pline->name);
- X last_line = pline->link;
- X free(pline);
- X }
- X fclose(fp_date_w);
- X fclose(fp_date_r);
- X line_root = NULL;
- X
- X /* rm active.times.o */
- X sprintf(buf,"%s.o", ACTIVE_TIMES_FILE);
- X unlink(buf);
- X /* mv active.times active.times.o */
- X link(ACTIVE_TIMES_FILE, buf);
- X unlink(ACTIVE_TIMES_FILE);
- X /* mv active.times.n active.times */
- X sprintf(buf, "%s.n", ACTIVE_TIMES_FILE);
- X link(buf, ACTIVE_TIMES_FILE);
- X unlink(buf);
- X}
- X
- void
- free_lines()
- X{
- X for (pline = line_root; pline; pline = last_line) {
- X if (pline->name) {
- X free(pline->name);
- X }
- X last_line = pline->link;
- X free(pline);
- X }
- X line_root = NULL;
- X}
- END_OF_FILE
- if test 10481 -ne `wc -c <'nntp/acttimes.c'`; then
- echo shar: \"'nntp/acttimes.c'\" unpacked with wrong size!
- fi
- # end of 'nntp/acttimes.c'
- fi
- if test -f 'nntp/xthread.patch' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'nntp/xthread.patch'\"
- else
- echo shar: Extracting \"'nntp/xthread.patch'\" \(11249 characters\)
- sed "s/^X//" >'nntp/xthread.patch' <<'END_OF_FILE'
- Here's a modified version of Tim Iverson's xthread patch for nntp. The code
- that goes into the nntp daemon has been only slightly modified for use with
- trn 2.0. The clientlib code has been entirely re-worked, however.
- X
- If you plan to build a trn that uses the XTHREAD extention for NNTP,
- you need to apply this patch from the root directory of your nntp source
- before compiling trn. Your server will need to be running a version of
- nntpd that supports the XTHREAD call. This can be either version 1.5.11t
- created by this patch, or version 1.5.11a created by Tim's patch.
- X
- To apply this:
- X cd nntp-1.5.11
- X patch -p < xthread.patch
- or unipatch < xthread.patch | patch -p
- X
- This patch changes the conf.h.dist file, so you will need to edit your
- conf.h file to add the new XTHREAD section. For example, you could:
- X
- X cd common
- X diff -c conf.h.dist.orig conf.h.dist | patch conf.h
- X
- and then edit conf.h to make sure THREAD_DIR is defined correctly. If
- you're not running C news, I also recommend that you set things up to use
- the active.times file, and use "mthreads -t" or the program acttimes to
- maintain it. This allows trn to use the NEWGROUPS call in nntp, and have
- it return accurate values.
- X
- Then you are ready to compile the new server with "make server" as usual.
- X
- Tim can be contacted at: iverson@xstor.com -/- uunet!xstor!iverson
- X--
- X \ /| / /|\/ /| /(_) Wayne Davison
- X(_)/ |/ /\|/ / |/ \ davison@borland.com
- X (W A Y N e)
- X
- Index: common/version.c
- Prereq: "1.5.11
- X@@ -3,3 +3,3 @@
- X */
- X
- X-char nntp_version[] = "1.5.11 (10 February 1991)";
- X+char nntp_version[] = "1.5.11t (16 November 1991)";
- Index: common/clientlib.c
- X@@ -606,4 +606,74 @@
- X
- X
- X+static long rawbytes = -1; /* bytes remaining to be transfered */
- X+
- X+/*
- X+ * rawcheck_server -- get a line of text from the server, interpreting
- X+ * it as a status message for a raw (binary) command. Call this once
- X+ * before calling rawget_server() for the actual data transfer.
- X+ *
- X+ * Parameters: "string" has the buffer space for the
- X+ * line received.
- X+ * "size" is the size of the buffer.
- X+ *
- X+ * Returns: -1 on error, otherwise the length of the raw data.
- X+ *
- X+ * Side effects: Talks to server, changes contents of "string".
- X+ */
- X+long
- X+rawcheck_server(string, size)
- X+char *string;
- X+int size;
- X+{
- X+ /* try to get the status line and the status code */
- X+ if (get_server(string, size) || *string != CHAR_OK)
- X+ return rawbytes = -1;
- X+
- X+ /* try to get the number of bytes being transfered */
- X+ if (sscanf(string, "%*d%ld", &rawbytes) != 1)
- X+ return rawbytes = -1;
- X+ return rawbytes;
- X+}
- X+
- X+
- X+/*
- X+ * rawget_server -- read data from the server in raw format. This call must
- X+ * follow an appropriate put_server command and a rawcheck_server call.
- X+ *
- X+ * Parameters: "buf" is the buffer for the data to receive.
- X+ * "n" is the size of the buffer.
- X+ *
- X+ * Returns: 0 on EOF, otherwise the length of the read.
- X+ *
- X+ * Side effects: Talks to server, changes contents of "buf".
- X+ */
- X+long
- X+rawget_server(buf, n)
- X+void *buf;
- X+long n;
- X+{
- X+ /* if no bytes to read, then just return EOF */
- X+ if (rawbytes < 0)
- X+ return 0;
- X+
- X+ /* try to read some data from the server */
- X+ if (rawbytes) {
- X+ n = fread(buf, 1, n > rawbytes ? rawbytes : n, ser_rd_fp);
- X+ rawbytes -= n;
- X+ } else
- X+ n = 0;
- X+
- X+ /* if no more left, then fetch the end-of-command signature */
- X+ if (!rawbytes)
- X+ {
- X+ char buf[5]; /* "\r\n.\r\n" */
- X+
- X+ fread(buf, 1, 5, ser_rd_fp);
- X+ rawbytes = -1;
- X+ }
- X+ return n;
- X+}
- X+
- X+
- X /*
- X * close_server -- close the connection to the server, after sending
- X@@ -633,3 +703,2 @@
- X (void) fclose(ser_rd_fp);
- X }
- X-
- Index: common/clientlib.h
- X@@ -9,3 +9,5 @@
- X extern void put_server();
- X extern int get_server();
- X+extern long rawcheck_server();
- X+extern long rawget_server();
- X extern void close_server();
- Index: common/conf.h.dist
- X@@ -95,4 +95,19 @@
- X /* loaded already, defining this may be a bad idea */
- X
- X+/* XTHREAD defines: if XTHREAD is defined, THREAD_DIR controls where the
- X+ * thread files will be read from.
- X+ */
- X+#define XTHREAD /* Optional XTHREAD command. This allows trn to
- X+ * keep all data on the server. */
- X+
- X+/* Leave this undefined to indicate that thread files go in the spool
- X+ * directory. However, if you want a separate hierarchy of thread files,
- X+ * define it here.
- X+ */
- X+/*#define THREAD_DIR "/usr/spool/threads" /* base directory */
- X+
- X+/* if LONG_THREAD_NAMES & THREAD_DIR are defined, create threads in one dir */
- X+#undef LONG_THREAD_NAMES /* not for short-name systems */
- X+
- X /* Things that vary in network implementations */
- X #define SUBNET /* If you have 4.3 subnetting */
- X@@ -333,4 +348,22 @@
- X # endif
- X # endif
- X+#endif
- X+
- X+#ifdef XTHREAD
- X+# ifdef THREAD_DIR
- X+# ifdef LONG_THREAD_NAMES
- X+# undef SUFFIX
- X+# else
- X+# ifndef SUFFIX
- X+# define SUFFIX ".th"
- X+# endif
- X+# endif
- X+# else
- X+# define THREAD_DIR SPOOLDIR
- X+# ifndef SUFFIX
- X+# define SUFFIX "/.thread"
- X+# endif
- X+# undef LONG_THREAD_NAMES
- X+# endif
- X #endif
- X
- Index: common/nntp.h
- X@@ -49,4 +49,5 @@
- X #define OK_AUTHSYS 280 /* Authorization system ok */
- X #define OK_AUTH 281 /* Authorization (user/pass) ok */
- X+#define OK_BIN 282 /* binary data follows */
- X
- X #define CONT_XFER 335 /* Continue to send article */
- Index: server/Makefile
- X@@ -7,5 +7,5 @@
- X newgroups.o newnews.o nextlast.o ngmatch.o post.o parsit.o scandir.o \
- X slave.o spawn.o strcasecmp.o subnet.o time.o xhdr.o fakesyslog.o \
- X- batch.o auth.o timer.o ../common/version.o
- X+ batch.o auth.o timer.o xthread.o ../common/version.o
- X
- X SRVRSRC = main.c serve.c access.c access_inet.c access_dnet.c active.c \
- X@@ -13,5 +13,5 @@
- X newgroups.c newnews.c nextlast.c ngmatch.c post.c parsit.c scandir.c \
- X slave.c spawn.c strcasecmp.c subnet.c time.c xhdr.c fakesyslog.c \
- X- batch.c auth.c timer.c ../common/version.c
- X+ batch.c auth.c timer.c xthread.c ../common/version.c
- X
- X SRVRINC = common.h ../common/conf.h ../common/nntp.h timer.h
- Index: server/common.h
- X@@ -164,4 +164,9 @@
- X extern char inews[];
- X extern char rnews[];
- X+
- X+#ifdef XTHREAD
- X+extern char *threaddir;
- X+extern char *threadfile;
- X+#endif
- X
- X extern char **group_array;
- Index: server/globals.c
- X@@ -27,4 +27,9 @@
- X char rnews[] = RNEWS;
- X
- X+#ifdef XTHREAD
- X+char *threaddir = THREAD_DIR;
- X+char *threadfile = NULL;
- X+#endif
- X+
- X /*
- X * Other random externals.
- Index: server/group.c
- X@@ -5,4 +5,8 @@
- X #include "common.h"
- X
- X+#ifdef XTHREAD
- X+extern char *thread_name();
- X+#endif
- X+
- X /*
- X * GROUP newsgroup
- X@@ -97,4 +101,8 @@
- X
- X ingroup = 1;
- X+
- X+#ifdef XTHREAD
- X+ threadfile = thread_name(argv[1]);
- X+#endif
- X
- X while ((cp = index(argv[1], '/')) != (char *) NULL)
- Index: server/help.c
- X@@ -22,6 +22,13 @@
- X printf("STAT NEWGROUPS HELP\r\n");
- X printf("IHAVE NEWNEWS SLAVE\r\n");
- X- printf("\r\nAdditionally, the following extention is supported:\r\n\r\n");
- X+#if defined(XHDR) || defined(XTHREAD)
- X+ printf("\r\nAdditionally, the following extentions are supported:\r\n\r\n");
- X+# ifdef XHDR
- X printf("XHDR Retrieve a single header line from a range of articles.\r\n");
- X+# endif XHDR
- X+# ifdef XTHREAD
- X+ printf("XTHREAD Retrieve trn thread file for the current group.\r\n");
- X+# endif
- X+#endif
- X printf("\r\n");
- X printf("Bugs to Stan Barber (Internet: nntp@tmc.edu; UUCP: ...!bcm!nntp)\r\n");
- Index: server/serve.c
- X@@ -27,5 +27,5 @@
- X extern int ahbs(), group(), help(), ihave();
- X extern int list(), newgroups(), newnews(), nextlast(), post();
- X-extern int slave(), stat(), xhdr();
- X+extern int slave(), stat(), xhdr(), xthread();
- X
- X extern int errno;
- X@@ -62,4 +62,7 @@
- X "xhdr", 0, xhdr,
- X #endif XHDR
- X+#ifdef XTHREAD
- X+ "xthread", 0, xthread,
- X+#endif
- X };
- X #define NUMCMDS (sizeof(cmdtbl) / sizeof(struct cmdent))
- Index: server/xthread.c
- X@@ -0,0 +1,152 @@
- X+/* This file (and only this file - not the entire nntp distribution) is
- X+ * hereby placed in the public domain. Use it as you see fit, but if you
- X+ * manage to find some wonderful use for this code elsewhere, it would be
- X+ * nice to receive mention for it.
- X+ *
- X+ * - Tim Iverson
- X+ * iverson@xstor.com -/- uunet!xstor!iverson
- X+ * 3/28/91
- X+ * modified by Wayne Davison (davison@borland.com) to work with trn 2.0
- X+ * 10/6/91
- X+ */
- X+
- X+#include "common.h"
- X+
- X+#ifdef XTHREAD
- X+
- X+# ifdef __GNUC__
- X+# define alloca __builtin_alloca
- X+# endif
- X+
- X+
- X+/* Usage: XTHREAD [DBINIT|THREAD]
- X+ *
- X+ * DBINIT dump the contents of the db.init file to stdout
- X+ * THREAD dump the contents of the thread file for the current
- X+ * newsgroup to stdout (default if no argument).
- X+ *
- X+ * N.B. These two files are not ascii and no attempt is made at converting
- X+ * native byte size to any type of standard whatsoever. This'll have
- X+ * to be fixed if this command is to be integrated into the protocol.
- X+ *
- X+ * This command is not documented in rfc977.
- X+ */
- X+
- X+void
- X+xthread(argc, argv)
- X+int argc;
- X+char *argv[];
- X+{
- X+ register FILE *fp;
- X+ struct stat s;
- X+ char *buf, *file, *what;
- X+
- X+ /* can't transfer threads, only read 'em */
- X+ if (!canread)
- X+ {
- X+ printf("%d You only have permission to transfer, sorry.\r\n",
- X+ ERR_ACCESS);
- X+ (void) fflush(stdout);
- X+ return;
- X+ }
- X+
- X+ /* "parse" the argument */
- X+ if (argc == 2 && !strcasecmp(argv[1], "dbinit"))
- X+ {
- X+ char *cp, *thread_name();
- X+
- X+ file = thread_name("*******");
- X+ what = "db.init";
- X+ for (cp = file; *cp != '*'; cp++)
- X+ ;
- X+ strcpy(cp, what);
- X+ }
- X+ else if (argc == 1 || argc == 2 && !strcasecmp(argv[1], "thread"))
- X+ {
- X+ if (!threadfile)
- X+ {
- X+ printf("%d You are not currently in a newsgroup.\r\n",
- X+ ERR_NCING);
- X+ (void) fflush(stdout);
- X+ return;
- X+ }
- X+ file = threadfile;
- X+ what = "thread";
- X+ }
- X+ else
- X+ {
- X+ printf("%d Usage: XTHREAD [DBINIT|THREAD]\r\n", ERR_CMDSYN);
- X+ (void) fflush(stdout);
- X+ return;
- X+ }
- X+
- X+ /* try to open the file to be transfered */
- X+ if (!(fp = fopen(file, "r")))
- X+ {
- X+ printf("%d %s file is not available.\r\n", ERR_FAULT, what);
- X+ (void) fflush(stdout);
- X+#ifdef SYSLOG
- X+ if (!strcmp(what, "db.init"))
- X+ syslog(LOG_ERR, "xthread: fopen %s: %m", file);
- X+#endif
- X+ return;
- X+ }
- X+
- X+ /* tell 'em how much binary data is coming down the pike */
- X+ fstat(fileno(fp), &s);
- X+ printf("%d %u bytes of %s file follows verbatim (binary!)\r\n",
- X+ OK_BIN, s.st_size, what);
- X+
- X+ /* copy the file verbatim to stdout */
- X+#ifdef __GNUC__
- X+ if (buf = alloca(s.st_size))
- X+ {
- X+ /* ah-so! got lotsa memoree */
- X+ read(fileno(fp), buf, s.st_size);
- X+ fwrite(buf, s.st_size, 1, stdout);
- X+ }
- X+ else
- X+#endif
- X+ {
- X+ int bytes;
- X+ char buf[BUFSIZ];
- X+
- X+ while (bytes = fread(buf, 1, sizeof buf, fp))
- X+ fwrite(buf, bytes, 1, stdout);
- X+ }
- X+
- X+ fputs("\r\n.\r\n", stdout);
- X+ fflush(stdout);
- X+ fclose(fp);
- X+}
- X+
- X+/* Change a newsgroup name into the name of the thread data file. We
- X+** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES
- X+** is defined), prepend the path, and append the '/.thread' (or '.th') on to
- X+** the end.
- X+*/
- X+char *
- X+thread_name(group)
- X+char *group;
- X+{
- X+ static char name_buff[MAXPATHLEN];
- X+#ifndef LONG_THREAD_NAMES
- X+ char group_buff[512];
- X+ register char *ptr;
- X+
- X+ strcpy(group_buff, group);
- X+ ptr = group = group_buff;
- X+ while ((ptr = index(ptr, '.'))) {
- X+ *ptr = '/';
- X+ }
- X+#endif
- X+#ifdef SUFFIX
- X+ sprintf(name_buff, "%s/%s%s", threaddir, group, SUFFIX);
- X+#else
- X+ sprintf(name_buff, "%s/%s", threaddir, group);
- X+#endif
- X+
- X+ return name_buff;
- X+}
- X+
- X+#endif /* not XTHREAD */
- END_OF_FILE
- if test 11249 -ne `wc -c <'nntp/xthread.patch'`; then
- echo shar: \"'nntp/xthread.patch'\" unpacked with wrong size!
- fi
- # end of 'nntp/xthread.patch'
- fi
- if test -f 'rthreads.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'rthreads.c'\"
- else
- echo shar: Extracting \"'rthreads.c'\" \(10171 characters\)
- sed "s/^X//" >'rthreads.c' <<'END_OF_FILE'
- X/* $Id: rthreads.c,v 4.4.3.1 1991/11/22 04:12:18 davison Trn $
- X**
- X** $Log: rthreads.c,v $
- X** Revision 4.4.3.1 1991/11/22 04:12:18 davison
- X** Trn Release 2.0
- X**
- X*/
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#include "intrp.h"
- X#include "rn.h"
- X#include "ngdata.h"
- X#include "rcln.h"
- X#include "bits.h"
- X#include "util.h"
- X#ifndef USETMPTHREAD
- X#include "rcstuff.h"
- X#endif
- X#ifdef SERVER
- X#include "server.h"
- X#endif
- X
- X#ifdef USETHREADS
- X#include "threads.h"
- X#include "INTERN.h"
- X#include "rthreads.h"
- X
- static FILE *fp;
- X
- static char *strings = Nullch;
- X
- static bool tmpthread_created = FALSE;
- X
- static int read_item();
- static void safefree();
- X
- X/* Initialize our thread code by determining the byte-order of the thread
- X** files and our own current byte-order. If they differ, set flags to let
- X** the read code know what we'll need to translate.
- X*/
- void
- thread_init()
- X{
- X int i;
- X#ifdef XTHREAD
- X long size;
- X#endif
- X
- X tmpthread_file = savestr(filexp("/tmp/thread.%$"));
- X
- X word_same = long_same = TRUE;
- X#ifdef XTHREAD
- X sprintf(ser_line, "XTHREAD DBINIT");
- X# ifdef DEBUGGING
- X if (debug & DEB_NNTP) {
- X printf(">%s\n", ser_line) FLUSH;
- X }
- X# endif
- X put_server(ser_line);
- X rawcheck_server(ser_line, sizeof ser_line);
- X# ifdef DEBUGGING
- X if (debug & DEB_NNTP) {
- X printf("<%s\n", ser_line) FLUSH;
- X }
- X# endif
- X size = rawget_server((char*)&mt_bmap, sizeof (BMAP));
- X if (size >= sizeof (BMAP) - 1) {
- X#else /* !XTHREAD */
- X if ((fp = fopen(filexp(DBINIT), FOPEN_RB)) != Nullfp
- X && fread((char*)&mt_bmap, 1, sizeof(BMAP), fp) >= sizeof (BMAP) - 1) {
- X#endif
- X if (mt_bmap.version != DB_VERSION) {
- X printf("\nThread database is the wrong version -- ignoring it.\n")
- X FLUSH;
- X use_threads = FALSE;
- X }
- X mybytemap(&my_bmap);
- X for (i = 0; i < sizeof (LONG); i++) {
- X if (i < sizeof (WORD)) {
- X if (my_bmap.w[i] != mt_bmap.w[i]) {
- X word_same = FALSE;
- X }
- X }
- X if (my_bmap.l[i] != mt_bmap.l[i]) {
- X long_same = FALSE;
- X }
- X }
- X } else {
- X printf("\ndb.init read failed -- assuming no byte-order translations.\n\n") FLUSH;
- X }
- X#ifdef XTHREAD
- X while (rawget_server(ser_line, sizeof ser_line)) {
- X ; /* trash any extraneous bytes */
- X }
- X#else
- X if (fp != Nullfp) {
- X fclose(fp);
- X }
- X#endif
- X}
- X
- X/* Open a thread file to make use of the data it contains. Everything is
- X** slurped into arrays and buffers, and some minor digesting of the data
- X** is performed to make it more palatable. Be sure to call unuse_data()
- X** before calling this again.
- X*/
- int
- use_data(thread_if_empty)
- bool_int thread_if_empty;
- X{
- X register int i, j, k;
- X register char *ptr;
- X char *threadname;
- X bool already_tried = FALSE;
- X ART_NUM last, first;
- X
- X last = getngsize(ng);
- X first = getabsfirst(ng, last);
- X if (last < first) {
- X return 0;
- X }
- X if (tmpthread_group && strEQ(ngname, tmpthread_group)) {
- X threadname = tmpthread_file;
- X } else {
- X#ifdef XTHREAD /* use remote thread file? */
- X long size;
- X
- X threadname = Nullch; /* assume we fail */
- X sprintf(ser_line, "XTHREAD THREAD");
- X# ifdef DEBUGGING
- X if (debug & DEB_NNTP) {
- X printf(">%s\n", ser_line) FLUSH;
- X }
- X# endif
- X put_server(ser_line);
- X size = rawcheck_server(ser_line, sizeof ser_line);
- X# ifdef DEBUGGING
- X if (debug & DEB_NNTP) {
- X printf("<%s\n", ser_line) FLUSH;
- X }
- X# endif
- X if (size < 0) {
- X fp = Nullfp;
- X } else {
- X char *tmpbuf;
- X long bufsize;
- X
- X# ifdef SPEEDOVERMEM
- X bufsize = size ? size : 1;
- X# else
- X bufsize = 16384;
- X# endif
- X fp = fopen(tmpthread_file, "w");
- X tmpbuf = safemalloc(bufsize);
- X while ((size = rawget_server(tmpbuf, bufsize)) != 0) {
- X if (fp != Nullfp) {
- X /* write it out unbuffered */
- X write(fileno(fp), tmpbuf, size);
- X }
- X }
- X free(tmpbuf);
- X if (fp != Nullfp) {
- X fclose(fp);
- X growstr(&tmpthread_group, &tmpthread_glen,
- X strlen(ngname) + 1);
- X strcpy(tmpthread_group, ngname);
- X tmpthread_created = FALSE;
- X threadname = tmpthread_file;
- X }
- X }
- X#else /* !XTHREAD */
- X threadname = thread_name(ngname);
- X#endif
- X }
- try_to_use:
- X#ifdef XTHREAD
- X if (!threadname || (fp = fopen(threadname, FOPEN_RB)) == Nullfp) {
- X if (threadname && errno != ENOENT) {
- X#else
- X if ((fp = fopen(threadname, FOPEN_RB)) == Nullfp) {
- X if (errno != ENOENT) {
- X#endif
- X printf("\n\nOpen failed for thread data.\n");
- X }
- X bzero(&total, sizeof (TOTAL));
- X if (!thread_if_empty || already_tried) {
- X return 0;
- X }
- X } else if (fread((char*)&total, 1, sizeof(TOTAL), fp) < sizeof(TOTAL)) {
- X fclose(fp);
- X bzero(&total, sizeof (TOTAL));
- X if (already_tried) {
- X return 0;
- X }
- X } else if (threadname != tmpthread_file || !tmpthread_created) {
- X lp_bmap(&total.first, 4);
- X wp_bmap(&total.root, 5);
- X } else if (!total.root) {
- X fclose(fp);
- X return 0;
- X }
- X
- X if (total.last < last) {
- X#ifdef USETMPTHREAD
- X char cmd[512];
- X ART_NUM max = last - first + 1;
- X
- X if (fp) {
- X fclose(fp);
- X }
- X sprintf(cmd, "tmpthread %s %s %ld %ld %ld %s",
- X#ifdef XTHREAD
- X threadname == tmpthread_file ?
- X (tmpthread_created ? "-t" : "-T") : nullstr,
- X#else
- X threadname == tmpthread_file ? "-t" : nullstr,
- X#endif
- X ngname, (long)last, (long)first, (long)max, tmpthread_file);
- X if (system(filexp(cmd))) {
- X printf("\n\nFailed to thread data -- continuing unthreaded.\n");
- X if (tmpthread_group) {
- X *tmpthread_group = '\0';
- X }
- X return 0;
- X }
- X growstr(&tmpthread_group, &tmpthread_glen, strlen(ngname) + 1);
- X strcpy(tmpthread_group, ngname);
- X threadname = tmpthread_file;
- X tmpthread_created = TRUE;
- X already_tried = TRUE;
- X goto try_to_use;
- X#else /* !USETMPTHREAD */
- X if (lastart > total.last) {
- X tobethreaded = last - total.last;
- X toread[ng] -= tobethreaded;
- X lastart = total.last;
- X }
- X#endif /* !USETMPTHREAD */
- X }
- X
- X if (total.last > lastart) {
- X#ifdef SERVER
- X fclose(actfp);
- X ngdata_init(); /* re-grab the active file */
- X#endif
- X grow_ctl(total.last); /* sets lastart */
- X ngmax[ng] = lastart; /* ensure getngsize() knows the new maximum */
- X }
- X
- X if (!read_item(&author_cnts, (MEM_SIZE)total.author * sizeof (WORD))
- X || !read_item(&strings, (MEM_SIZE)total.string1)
- X || !read_item(&subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD))
- X || !read_item(&p_roots, (MEM_SIZE)total.root * sizeof (PACKED_ROOT))
- X || !read_item(&p_articles, (MEM_SIZE)total.article * sizeof (PACKED_ARTICLE))) {
- X printf("\n\nRead failed for thread data -- continuing unthreaded.\n");
- X fclose(fp);
- X unuse_data(0);
- X return 0;
- X }
- X fclose(fp);
- X
- X if ((threadname != tmpthread_file || !tmpthread_created)
- X && (!word_same || !long_same)) {
- X wp_bmap(author_cnts, total.author);
- X wp_bmap(subject_cnts, total.subject);
- X for (i = 0; i < total.root; i++) {
- X lp_bmap(&p_roots[i].root_num, 1);
- X wp_bmap(&p_roots[i].articles, 3);
- X }
- X for (i = 0; i < total.article; i++) {
- X lp_bmap(&p_articles[i].num, 2);
- X wp_bmap(&p_articles[i].subject, 8);
- X }
- X }
- X
- X#ifndef lint
- X author_ptrs = (char **)safemalloc(total.author * sizeof (char **));
- X subject_ptrs = (char **)safemalloc(total.subject * sizeof (char **));
- X root_subjects = (WORD *)safemalloc(total.root * sizeof (WORD));
- X root_article_cnts = (WORD *)safemalloc(total.root * sizeof (WORD));
- X#endif
- X selected_roots = safemalloc(total.root * sizeof (char));
- X
- X bzero(root_article_cnts, total.root * sizeof (WORD));
- X bzero(selected_roots, total.root * sizeof (char));
- X
- X for (i = 0, ptr = strings; i < total.author; i++) {
- X author_ptrs[i] = ptr;
- X ptr += strlen(ptr) + 1;
- X }
- X
- X for (i = 0, j = 0; i < total.root; i++) {
- X root_subjects[i] = j;
- X k = p_roots[i].subject_cnt;
- X while (k--) {
- X root_article_cnts[i] += subject_cnts[j];
- X subject_ptrs[j++] = ptr;
- X ptr += strlen(ptr) + 1;
- X }
- X if (saved_selections) {
- X for (k = 0; k < selected_root_cnt; k++) {
- X if (p_roots[i].root_num == saved_selections[k]) {
- X selected_roots[i] = 1;
- X break;
- X }
- X }
- X }
- X }
- X count_roots(!saved_selections);
- X
- X /* Try to clean up the bitmap if articles are missing. */
- X if (unthreaded) {
- X char *newarea, *oldarea = ctlarea;
- X extern MEM_SIZE ctlsize;
- X
- X newarea = ctlarea = safemalloc(ctlsize);
- X bzero(ctlarea, ctlsize);
- X for (i = total.article, p_art = p_articles; i--; p_art++) {
- X if (p_art->num >= firstbit) {
- X ctl_set(p_art->num);
- X }
- X }
- X for (i = firstbit; i <= lastart; i++) {
- X if (!ctl_read(i)) {
- X ctlarea = oldarea;
- X oneless(i);
- X ctlarea = newarea;
- X }
- X }
- X ctlarea = oldarea;
- X free(newarea);
- X p_art = Nullart;
- X count_roots(FALSE);
- X }
- X safefree(&saved_selections);
- X select_page = 0;
- X return 1;
- X}
- X
- X/* A shorthand for reading a chunk of the file into a malloced array.
- X*/
- static int
- read_item(dest, len)
- char **dest;
- MEM_SIZE len;
- X{
- X int ret;
- X
- X *dest = safemalloc(len);
- X ret = fread(*dest, 1, (int)len, fp);
- X if (ret != len) {
- X free(*dest);
- X *dest = Nullch;
- X return 0;
- X }
- X return 1;
- X}
- X
- X/* Free some memory if it hasn't already been freed.
- X*/
- static void
- safefree(pp)
- char **pp;
- X{
- X if (*pp) {
- X free(*pp);
- X *pp = Nullch;
- X }
- X}
- X
- X/* Discard the thread data that we received through the use_data() call.
- X** If "save_selections" is non-zero, we'll try to remember which roots
- X** are currently selected long enough for the use_data() call to re-use
- X** them. Only do this when you are going to re-open the same data file
- X** immediately with use_data() (presumably because the data has been
- X** updated while we were using it).
- X*/
- void
- unuse_data(save_selections)
- bool_int save_selections;
- X{
- X int i, j;
- X
- X if (save_selections) {
- X#ifndef lint
- X saved_selections
- X = (ART_NUM *)safemalloc(selected_root_cnt * sizeof (ART_NUM));
- X#endif
- X for (i = 0, j = 0; i < total.root; i++) {
- X if (selected_roots[i]) {
- X saved_selections[j++] = p_roots[i].root_num;
- X }
- X }
- X } else {
- X selected_root_cnt = selected_count = 0;
- X }
- X safefree(&p_roots);
- X safefree(&root_subjects);
- X safefree(&author_cnts);
- X safefree(&subject_cnts);
- X safefree(&author_ptrs);
- X safefree(&subject_ptrs);
- X safefree(&root_article_cnts);
- X safefree(&selected_roots);
- X safefree(&p_articles);
- X safefree(&strings);
- X
- X p_art = curr_p_art = Nullart;
- X init_tree(); /* free any tree lines */
- X
- X bzero(&total, sizeof (TOTAL));
- X}
- X
- X#endif /* USETHREADS */
- END_OF_FILE
- if test 10171 -ne `wc -c <'rthreads.c'`; then
- echo shar: \"'rthreads.c'\" unpacked with wrong size!
- fi
- # end of 'rthreads.c'
- fi
- if test -f 'term.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'term.h'\"
- else
- echo shar: Extracting \"'term.h'\" \(9410 characters\)
- sed "s/^X//" >'term.h' <<'END_OF_FILE'
- X/* $Id: term.h,v 4.4 1991/09/09 20:27:37 sob Exp sob $
- X *
- X * $Log: term.h,v $
- X * Revision 4.4 1991/09/09 20:27:37 sob
- X * release 4.4
- X *
- X *
- X *
- X */
- X/* This software is Copyright 1991 by Stan Barber.
- X *
- X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
- X * use this software as long as: there is no monetary profit gained
- X * specifically from the use or reproduction of this software, it is not
- X * sold, rented, traded or otherwise marketed, and this copyright notice is
- X * included prominently in any copy made.
- X *
- X * The author make no claims as to the fitness or correctness of this software
- X * for any use whatsoever, and it is provided as is. Any use of this software
- X * is at the user's own risk.
- X */
- X
- X#ifdef PUSHBACK
- XEXT char circlebuf[PUSHSIZE];
- XEXT int nextin INIT(0);
- XEXT int nextout INIT(0);
- X#ifdef PENDING
- X#ifdef FIONREAD
- XEXT long iocount INIT(0);
- X#ifndef lint
- X#define input_pending() (nextin!=nextout || (ioctl(0, FIONREAD, &iocount),(int)iocount))
- X#else
- X#define input_pending() bizarre
- X#endif /* lint */
- X#else /* FIONREAD */
- X#ifdef RDCHK
- X#define input_pending() (rdchk(0) > 0) /* boolean only */
- X#else /* RDCHK */
- int circfill();
- XEXT int devtty INIT(0);
- X#ifndef lint
- X#define input_pending() (nextin!=nextout || circfill())
- X#else
- X#define input_pending() bizarre
- X#endif /* lint */
- X#endif /* RDCHK */
- X#endif /* FIONREAD */
- X#else /* PENDING */
- X#ifndef lint
- X#define input_pending() (nextin!=nextout)
- X#else
- X#define input_pending() bizarre
- X#endif /* lint */
- X#endif /* PENDING */
- X#else /* PUSHBACK */
- X#ifdef PENDING
- X#ifdef FIONREAD /* must have FIONREAD or O_NDELAY for input_pending() */
- X#define read_tty(addr,size) read(0,addr,size)
- X#ifndef lint
- X#define input_pending() (ioctl(0, FIONREAD, &iocount),(int)iocount)
- X#else
- X#define input_pending() bizarre
- X#endif /* lint */
- XEXT long iocount INIT(0);
- X
- X#else /* FIONREAD */
- X
- X#ifdef RDCHK
- X#define input_pending() (rdchk(0) > 0) /* boolean only */
- X#else /* RDCHK */
- X
- XEXT int devtty INIT(0);
- XEXT bool is_input INIT(FALSE);
- XEXT char pending_ch INIT(0);
- X#ifndef lint
- X#define input_pending() (is_input || (is_input=read(devtty,&pending_ch,1)))
- X#else
- X#define input_pending() bizarre
- X#endif /* lint */
- X#endif /* RDCHK */
- X#endif /* FIONREAD */
- X#else /* PENDING */
- X#define read_tty(addr,size) read(0,addr,size)
- X#define input_pending() (FALSE)
- X#endif /* PENDING */
- X#endif /* PUSHBACK */
- X
- X/* stuff wanted by terminal mode diddling routines */
- X
- X#ifdef TERMIO
- XEXT struct termio _tty, _oldtty;
- X#else
- X# ifdef TERMIOS
- XEXT struct termios _tty, _oldtty;
- X# else
- XEXT struct sgttyb _tty;
- XEXT int _res_flg INIT(0);
- X# endif
- X#endif
- X
- XEXT int _tty_ch INIT(2);
- XEXT bool bizarre INIT(FALSE); /* do we need to restore terminal? */
- X
- X/* terminal mode diddling routines */
- X
- X#ifdef TERMIO
- X
- X#define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty))
- X#define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty))
- X#define echo() ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty))
- X#define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty))
- X#define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
- X#define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
- X#define savetty() (ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty))
- X#define resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty))
- X#define unflush_output()
- X
- X#else /* !TERMIO */
- X# ifdef TERMIOS
- X
- X#define crmode() ((bizarre=1), _tty.c_lflag &= ~ICANON,_tty.c_cc[VMIN]=1,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
- X#define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,tcsetattr(_tty_ch, TCSAFLUSH,&_tty))
- X#define echo() ((bizarre=1),_tty.c_lflag |= ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
- X#define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
- X#define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
- X#define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
- X#define savetty() (tcgetattr(_tty_ch, &_oldtty),tcgetattr(_tty_ch, &_tty))
- X#define resetty() ((bizarre=0),tcsetattr(_tty_ch, TCSAFLUSH, &_oldtty))
- X#define unflush_output()
- X
- X# else /* !TERMIOS */
- X
- X#define raw() ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty))
- X#define noraw() ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty))
- X#define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty))
- X#define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty))
- X#define echo() ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty))
- X#define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty))
- X#define nl() ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty))
- X#define nonl() ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty))
- X#define savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags)
- X#define resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty))
- X# ifdef LFLUSHO
- X# ifndef lint
- XEXT int lflusho INIT(LFLUSHO);
- X# else
- XEXT long lflusho INIT(LFLUSHO);
- X# endif /* lint */
- X#define unflush_output() (ioctl(_tty_ch,TIOCLBIC,&lflusho))
- X# else
- X#define unflush_output()
- X# endif /* LFLUSHO */
- X# endif /* TERMIOS */
- X
- X#endif /* TERMIO */
- X
- X#ifdef TIOCSTI
- X#ifdef lint
- X#define forceme(c) ioctl(_tty_ch,TIOCSTI,Null(long*)) /* ghad! */
- X#else
- X#define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */
- X#endif /* lint */
- X#else
- X#define forceme(c)
- X#endif
- X
- X/* termcap stuff */
- X
- X/*
- X * NOTE: if you don't have termlib you'll either have to define these strings
- X * and the tputs routine, or you'll have to redefine the macros below
- X */
- X
- X#ifdef HAVETERMLIB
- XEXT int GT; /* hardware tabs */
- XEXT char *BC INIT(Nullch); /* backspace character */
- XEXT char *UP INIT(Nullch); /* move cursor up one line */
- XEXT char *CR INIT(Nullch); /* get to left margin, somehow */
- XEXT char *VB INIT(Nullch); /* visible bell */
- XEXT char *CL INIT(Nullch); /* home and clear screen */
- XEXT char *CE INIT(Nullch); /* clear to end of line */
- XEXT char *TI INIT(Nullch); /* initialize terminal */
- XEXT char *TE INIT(Nullch); /* reset terminal */
- X#if defined(CLEAREOL) || defined(USETHREADS)
- XEXT char *CM INIT(Nullch); /* cursor motion */
- XEXT char *HO INIT(Nullch); /* home cursor */
- X#endif
- X#ifdef CLEAREOL
- XEXT char *CD INIT(Nullch); /* clear to end of display */
- X#endif /* CLEAREOL */
- XEXT char *SO INIT(Nullch); /* begin standout mode */
- XEXT char *SE INIT(Nullch); /* end standout mode */
- XEXT int SG INIT(0); /* blanks left by SO and SE */
- XEXT char *US INIT(Nullch); /* start underline mode */
- XEXT char *UE INIT(Nullch); /* end underline mode */
- XEXT char *UC INIT(Nullch); /* underline a character,
- X if that's how it's done */
- XEXT int UG INIT(0); /* blanks left by US and UE */
- XEXT bool AM INIT(FALSE); /* does terminal have automatic
- X margins? */
- XEXT bool XN INIT(FALSE); /* does it eat 1st newline after
- X automatic wrap? */
- XEXT char PC INIT(0); /* pad character for use by tputs() */
- X
- X#ifdef _POSIX_SOURCE
- XEXT speed_t outspeed INIT(0); /* terminal output speed, */
- X#else
- XEXT long outspeed INIT(0); /* for use by tputs() */
- X#endif
- X
- XEXT int LINES INIT(0), COLS INIT(0); /* size of screen */
- XEXT int just_a_sec INIT(960); /* 1 sec at current baud rate */
- X /* (number of nulls) */
- X
- X/* define a few handy macros */
- X
- X#define backspace() tputs(BC,0,putchr) FLUSH
- X#define clear() tputs(CL,LINES,putchr) FLUSH
- X#define erase_eol() tputs(CE,1,putchr) FLUSH
- X#ifdef CLEAREOL
- X#define clear_rest() tputs(CD,LINES,putchr) FLUSH
- X#define maybe_eol() if(erase_screen&&can_home_clear)tputs(CE,1,putchr) FLUSH
- X#endif /* CLEAREOL */
- X#define underline() tputs(US,1,putchr) FLUSH
- X#define un_underline() tputs(UE,1,putchr) FLUSH
- X#define underchar() tputs(UC,0,putchr) FLUSH
- X#define standout() tputs(SO,1,putchr) FLUSH
- X#define un_standout() tputs(SE,1,putchr) FLUSH
- X#define up_line() tputs(UP,1,putchr) FLUSH
- X#define carriage_return() tputs(CR,1,putchr) FLUSH
- X#define dingaling() tputs(VB,1,putchr) FLUSH
- X#else
- X ???????? /* up to you */
- X#endif
- X
- XEXT int page_line INIT(1); /* line number for paging in
- X print_line (origin 1) */
- X
- void term_init ANSI((void));
- void term_set ANSI((char *));
- X#ifdef PUSHBACK
- void pushchar ANSI((char_int));
- void mac_init ANSI((char *));
- void mac_line ANSI((char *,char *,int));
- void show_macros ANSI((void));
- X#endif
- char putchr ANSI((char_int)); /* routine for tputs to call */
- bool finish_command ANSI((int));
- void eat_typeahead ANSI((void));
- void settle_down ANSI((void));
- X#ifdef HAVETERMLIB
- void termlib_init ANSI((void));
- void termlib_reset ANSI((void));
- X#endif
- X#ifndef read_tty
- X int read_tty ANSI((char *,int));
- X#endif
- void underprint ANSI((char *));
- X#ifdef NOFIREWORKS
- X void no_sofire ANSI((void));
- X void no_ulfire ANSI((void));
- X#endif
- void getcmd ANSI((char *));
- int get_anything ANSI((void));
- void in_char ANSI((char *,char_int));
- int print_lines ANSI((char *,int));
- void page_init ANSI((void));
- void pad ANSI((int));
- void printcmd ANSI((void));
- void rubout ANSI((void));
- void reprint ANSI((void));
- X#if defined(CLEAREOL) || defined(USETHREADS)
- void home_cursor ANSI((void));
- X#endif
- X#ifdef USETHREADS
- void goto_line ANSI((int,int));
- X#endif
- X#ifdef SIGWINCH
- SIGRET winch_catcher ANSI((void));
- X#endif /* SIGWINCH */
- END_OF_FILE
- if test 9410 -ne `wc -c <'term.h'`; then
- echo shar: \"'term.h'\" unpacked with wrong size!
- fi
- # end of 'term.h'
- fi
- if test -f 'uudecode.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'uudecode.c'\"
- else
- echo shar: Extracting \"'uudecode.c'\" \(9893 characters\)
- sed "s/^X//" >'uudecode.c' <<'END_OF_FILE'
- X/* $Id: uudecode.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
- X *
- X * $Log: uudecode.c,v $
- X * Revision 4.4.2.1 1991/12/01 18:05:42 sob
- X * Patchlevel 2 changes
- X *
- X * Revision 4.4 1991/09/09 20:27:37 sob
- X * release 4.4
- X *
- X *
- X * Decode one or more uuencoded articles back to binary form.
- X * Adapted to rn 4.4 by Stan Barber
- X * Trn version created by Wayne Davison.
- X * Adapted from the nn version by Kim Storm.
- X * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
- X */
- X/*
- X * This software is Copyright 1991 by Stan Barber.
- X *
- X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
- X * use this software as long as: there is no monetary profit gained
- X * specifically from the use or reproduction of this software, it is not
- X * sold, rented, traded or otherwise marketed, and this copyright notice is
- X * included prominently in any copy made.
- X *
- X * The author make no claims as to the fitness or correctness of this software
- X * for any use whatsoever, and it is provided as is. Any use of this software
- X * is at the user's own risk.
- X */
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#include "respond.h"
- X#include "decode.h"
- X
- X#define MAXCHAR 256
- X#define NORMLEN 64 /* allows for 84 encoded chars per line */
- X
- X#define SEQMAX 'z'
- X#define SEQMIN 'a'
- X
- static char seqc;
- static int first, secnd, check, numl;
- X
- static char blank;
- static int chtbl[MAXCHAR], cdlen[NORMLEN + 3];
- static int state;
- static bool Xflag;
- static int expecting_part;
- X
- static int decode_line ANSI((char *));
- static void inittbls ANSI((void));
- static void gettable ANSI((FILE *));
- X
- X#define NO_ADVANCE 0x10
- X
- X#define FIND_BEGIN 0x01
- X#define AFTER_ERROR_FIND_BEGIN 0x02
- X#define DECODE_TEXT 0x03
- X#define SKIP_TRAILING (0x04 | NO_ADVANCE)
- X#define SKIP_LEADING 0x05
- X#define FOUND_END (0x06 | NO_ADVANCE)
- X#define DECODE_ERROR (0x07 | NO_ADVANCE)
- X#define OTHER_ERROR (0x08 | NO_ADVANCE)
- X#define NEW_BEGIN (0x09 | NO_ADVANCE)
- X
- void
- uud_start()
- X{
- X Xflag = FALSE;
- X expecting_part = 0;
- X seqc = SEQMAX;
- X check = 1;
- X first = 1;
- X secnd = 0;
- X state = FIND_BEGIN;
- X}
- X
- int
- uudecode(in)
- XFILE *in;
- X{
- X int mode, onedone, lens;
- X char buff[LBUFLEN];
- X
- X numl = onedone = 0;
- X
- X if (state == FIND_BEGIN)
- X inittbls();
- X
- X /*
- X * search for header or translation table line.
- X */
- X
- X while ((state & NO_ADVANCE) || fgets(buff, sizeof buff, in) != Nullch) {
- X numl++;
- X
- X switch (state) {
- X case NEW_BEGIN:
- X if (decode_fp != Nullfp) {
- X if (expecting_part) {
- X register int got_part = 0;
- X
- X if (strnEQ(buff + 6, "part ", 5)) {
- X register char *bp;
- X
- X for (bp = buff + 11; islower(*bp); bp++)
- X got_part = got_part * 26 + *bp - 'a';
- X }
- X if (expecting_part == got_part) {
- X state = DECODE_TEXT;
- X break;
- X }
- X printf("Expecting part %d; got part %d.\n",
- X expecting_part + 1, got_part + 1);
- X if (got_part) {
- X state = SKIP_LEADING;
- X return -1;
- X }
- X }
- X decode_end();
- X sleep(2);
- X Xflag = FALSE;
- X expecting_part = 0;
- X }
- X state = FIND_BEGIN;
- X /* fall thru */
- X
- X case FIND_BEGIN:
- X case AFTER_ERROR_FIND_BEGIN:
- X if (strnEQ(buff, "table", 5)) {
- X gettable(in);
- X continue;
- X }
- X
- X if (strnEQ(buff, "begin ", 6)
- X || strnEQ(buff, "Xbegin ", 7)) {
- X lens = strlen(buff)-1;
- X if (buff[lens] == '\n')
- X buff[lens] = '\0';
- X
- X if(sscanf(buff+6,"%o%s", &mode, decode_fname) != 2) {
- X register char *bp = buff + 6;
- X
- X if (*bp == ' ')
- X bp++;
- X if (strnEQ(bp, "part ", 5)) {
- X register int got_part = 0;
- X
- X for (bp = bp + 5; islower(*bp); bp++)
- X got_part = got_part * 26 + *bp - 'a';
- X printf("Expecting part 1; got part %d.\n",
- X got_part + 1);
- X return -1;
- X }
- X continue;
- X }
- X
- X Xflag = (*buff == 'X');
- X
- X sprintf(decode_dest, "%s/%s", extractdest, decode_fname);
- X
- X if ((decode_fp = fopen(decode_dest, FOPEN_WB)) == Nullfp) {
- X printf("Cannot create file: %s\n", decode_dest);
- X goto err;
- X }
- X chmod(decode_dest, mode);
- X printf("Decoding: %s\n", decode_fname);
- X state = DECODE_TEXT;
- X }
- X continue;
- X
- X case SKIP_LEADING:
- X state = decode_line(buff);
- X continue;
- X
- X case DECODE_TEXT:
- X state = decode_line(buff);
- X onedone = 1;
- X continue;
- X
- X case FOUND_END:
- X fclose(decode_fp);
- X decode_fp = Nullfp;
- X Xflag = FALSE;
- X expecting_part = 0;
- X state = FIND_BEGIN;
- X printf("Done.\n");
- X continue;
- X
- X case SKIP_TRAILING:
- X printf("(Continued)\n");
- X state = SKIP_LEADING;
- X return 0;
- X
- X case DECODE_ERROR:
- X state = SKIP_TRAILING;
- X continue;
- X
- X case OTHER_ERROR:
- X fclose(decode_fp);
- X decode_fp = Nullfp;
- X Xflag = FALSE;
- X expecting_part = 0;
- X state = AFTER_ERROR_FIND_BEGIN;
- X goto err;
- X }
- X }
- X
- X if (onedone) {
- X if (state == DECODE_TEXT) {
- X printf("(Continued)\n");
- X state = SKIP_LEADING;
- X }
- X return 0;
- X }
- X
- X if (state == AFTER_ERROR_FIND_BEGIN)
- X return -1;
- X printf("Couldn't find anything to decode.\n");
- X
- X err:
- X sleep(2);
- X return -1;
- X}
- X
- X/*
- X * decode one line and write it out using decode_fp
- X */
- X
- static int
- decode_line(buff)
- char *buff;
- X{
- X char outl[LBUFLEN];
- X register char *bp, *ut;
- X register int *trtbl = chtbl;
- X register int n;
- X register int blen; /* binary length (from decoded file) */
- X register int rlen; /* calculated input line length */
- X register int len; /* actual input line length */
- X register int dash; /* number of '-'s encountered on a line */
- X /* If it's too high, we reject the line */
- X
- X# define REJECT(buf,rlen,len) /* Comment for makedepend to \
- X ** ignore the backslash above */ \
- X ((*buf == 'M' && len > rlen + 5) \
- X || (*buf != 'M' && len != rlen && len != rlen+1) \
- X || (strnEQ(buf, "BEGIN", 5)) \
- X || (strnEQ(buf, "END", 3)))
- X
- X if (Xflag) {
- X if (*buff == 'X')
- X buff++;
- X else
- X *buff = 'x'; /* force a mis-parse of a non-x'ed line */
- X }
- X len = strlen(buff);
- X if (--len <= 0)
- X return state;
- X
- X buff[len] = '\0';
- X
- X /*
- X * Get the binary line length.
- X */
- X if ((blen = trtbl[buff[0]]) < 0) {
- X if (state == SKIP_LEADING) {
- X if (strnEQ(buff, "begin ", 6))
- X return NEW_BEGIN;
- X
- X return SKIP_LEADING;
- X }
- X /*
- X * end of uuencoded file ?
- X */
- X if (strnEQ(buff, "end", 3))
- X return FOUND_END;
- X
- X /*
- X * end of current file ? : get next one.
- X */
- X if (strnEQ(buff, "include ", 8)) {
- X for (bp = buff + 8; *bp; bp++) {
- X if (bp[0] == '.' && bp[1] == 'u') {
- X expecting_part = (bp[2] - 'a') * 26 + bp[3] - 'a';
- X break;
- X }
- X }
- X }
- X
- X /*
- X * trailing garbage
- X */
- X return SKIP_TRAILING;
- X }
- X
- X rlen = cdlen[blen];
- X if (state == SKIP_LEADING && REJECT(buff,rlen,len))
- X return SKIP_LEADING;
- X
- X /*
- X * Is it the empty line before the end line ?
- X */
- X if (blen == 0)
- X return state;
- X
- X if (REJECT(buff,rlen,len))
- X return SKIP_TRAILING;
- X
- X /*
- X * Pad with blanks.
- X */
- X for (bp = buff + len, n = rlen - len; --n >= 0; )
- X *bp++ = blank;
- X
- X /*
- X * Verify
- X */
- X for (n = rlen, bp = buff, dash = 0; --n >= 0; bp++) {
- X if (trtbl[*bp] < 0) {
- X if (state == SKIP_LEADING)
- X return SKIP_LEADING;
- X return DECODE_ERROR;
- X }
- X if (*bp == '-')
- X dash++;
- X }
- X if (dash * 100 / rlen > 33) /* more than 1/3 dashes? */
- X if (state == SKIP_LEADING)
- X return SKIP_LEADING; /* -> reject */
- X else
- X return SKIP_TRAILING;
- X
- X /*
- X * Check for uuencodes that append a 'z' to each line....
- X */
- X if (check)
- X if (secnd) {
- X secnd = 0;
- X if (buff[rlen] == SEQMAX)
- X check = 0;
- X } else if (first) {
- X first = 0;
- X secnd = 1;
- X if (buff[rlen] != SEQMAX)
- X check = 0;
- X }
- X
- X /*
- X * There we check.
- X */
- X if (check) {
- X if (buff[rlen] != seqc) {
- X if (state == SKIP_LEADING)
- X return SKIP_LEADING;
- X return DECODE_ERROR;
- X }
- X
- X if (--seqc < SEQMIN)
- X seqc = SEQMAX;
- X }
- X
- X /*
- X * output a group of 3 bytes (4 input characters).
- X * the input chars are pointed to by p, they are to
- X * be output to file f. blen is used to tell us not to
- X * output all of them at the end of the file.
- X */
- X ut = outl;
- X n = blen;
- X bp = &buff[1];
- X while (--n >= 0) {
- X *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
- X if (n > 0) {
- X *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2);
- X n--;
- X }
- X if (n > 0) {
- X *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
- X n--;
- X }
- X bp += 4;
- X }
- X if (fwrite(outl, 1, blen, decode_fp) <= 0) {
- X printf("Error on writing decoded file\n");
- X return OTHER_ERROR;
- X }
- X
- X return DECODE_TEXT;
- X}
- X
- X
- X
- X/*
- X * Install the table in memory for later use.
- X */
- static void
- inittbls()
- X{
- X register int i, j;
- X
- X /*
- X * Set up the default translation table.
- X */
- X for (i = 0; i < ' '; i++)
- X chtbl[i] = -1;
- X for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
- X chtbl[i] = j;
- X for (i = ' ' + 64; i < MAXCHAR; i++)
- X chtbl[i] = -1;
- X chtbl['`'] = chtbl[' ']; /* common mutation */
- X chtbl['~'] = chtbl['^']; /* another common mutation */
- X blank = ' ';
- X /*
- X * set up the line length table, to avoid computing lotsa * and / ...
- X */
- X cdlen[0] = 1;
- X for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
- X cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
- X}
- X
- static void
- gettable(in)
- XFILE *in;
- X{
- X char buff[LBUFLEN];
- X register int c, n = 0;
- X register char *cpt;
- X
- X for (c = 0; c < MAXCHAR; c++)
- X chtbl[c] = -1;
- X
- X for (;;) {
- X if (fgets(buff, sizeof buff, in) == Nullch) {
- X printf("EOF while in translation table.\n");
- X return;
- X }
- X numl++;
- X if (strnEQ(buff, "begin", 5)) {
- X printf("Incomplete translation table.\n");
- X return;
- X }
- X cpt = buff + strlen(buff) - 1;
- X *cpt = ' ';
- X while (*cpt == ' ') {
- X *cpt = 0;
- X cpt--;
- X }
- X cpt = buff;
- X while (c = *cpt) {
- X if (chtbl[c] != -1) {
- X printf("Duplicate char in translation table.\n");
- X return;
- X }
- X if (n == 0)
- X blank = c;
- X chtbl[c] = n++;
- X if (n >= 64)
- X return;
- X cpt++;
- X }
- X }
- X}
- X
- END_OF_FILE
- if test 9893 -ne `wc -c <'uudecode.c'`; then
- echo shar: \"'uudecode.c'\" unpacked with wrong size!
- fi
- # end of 'uudecode.c'
- fi
- echo shar: End of archive 4 \(of 13\).
- cp /dev/null ark4isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 13 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-